diff --git a/.gn b/.gn
index b7734ea..e41eb038 100644
--- a/.gn
+++ b/.gn
@@ -74,7 +74,6 @@
   "//chrome/notification_helper:*",  # 4 errors
   "//chrome/test:*",  # 2682 errors
 
-  "//clank/third_party/gvr_shim:*",  # 1 error
   "//extensions/browser/api/declarative:*",  # 20 errors
   "//extensions/browser/api/declarative_net_request:*",  # 18 errors
   "//extensions/browser/api/declarative_webrequest:*",  # 29 errors
@@ -123,15 +122,7 @@
   "//third_party/icu/*",
   "//third_party/libvpx:*",  # 164 errors
   "//third_party/libwebp:*",  # 80 errors, https://crbug.com/800762
-  "//third_party/openscreen/src/cast/common:*",  # 4 errors
-  "//third_party/openscreen/src/cast/receiver:*",  # 1 error
-  "//third_party/openscreen/src/cast/streaming:*",  # 66 errors
   "//third_party/openscreen/src/discovery:*",  # 36 errors
-  "//third_party/openscreen/src/osp/impl/quic:*",  # 16 errors
-  "//third_party/openscreen/src/osp/msgs:*",  # 5 errors
-  "//third_party/openscreen/src/osp/public:*",  # 1 error
-  "//third_party/openscreen/src/osp:*",  # 13 errors
-  "//third_party/openscreen/src/util:*",  # 29 errors
 
   # //v8/*, https://crbug.com/v8/7330
   "//v8/src/inspector:*",  # 20 errors
diff --git a/DEPS b/DEPS
index ad955fb..53f477f2 100644
--- a/DEPS
+++ b/DEPS
@@ -184,7 +184,7 @@
   'llvm_force_head_revision': False,
 
   # reclient CIPD package version
-  'reclient_version': 're_client_version:0.19.3.3b3042c',
+  'reclient_version': 're_client_version:0.20.1.c4bbd2f',
 
   'android_git': 'https://android.googlesource.com',
   'aomedia_git': 'https://aomedia.googlesource.com',
@@ -199,11 +199,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '140adc67621956d787c5580b2fc23a4fb8fba31a',
+  'skia_revision': 'b849f7a791451bfaf7d8b9412ad241296101ceb8',
   # 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': 'ddadcacd13fae633df256fb967e61d4dbf1c8083',
+  'v8_revision': '45ea66edc82419c79bba183762142785bb5a72e2',
   # 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.
@@ -211,7 +211,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': '0f74ae58ed185108ec38a4cb038ef3c31acd9ff4',
+  'angle_revision': 'a8a2a71b3ab5e535def6239997f6f24da918556b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -226,7 +226,7 @@
   #
   # Note this revision should be updated with
   # third_party/boringssl/roll_boringssl.py, not roll-dep.
-  'boringssl_revision': 'c47bfce062cc5a1b462176be626338224ae2a346',
+  'boringssl_revision': 'ce9b002ebd0491a8dd802e208814360ce781f32b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '4de55da44c4b511989359381e8b7a88a01fe0634',
+  'nacl_revision': '82ac8c0a6f0d3ffc843c693dd5149a356b866ae5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -278,7 +278,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'ea45672fe179b3b841397ca11fb5a89b6e4828a9',
+  'devtools_frontend_revision': 'fcfa1e2cc0237111eaaae616a696767e71164090',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -318,11 +318,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': 'e0a588752c827daef74960bbc80d7f5c4f73c4dd',
+  'dawn_revision': '7e80cce1a9065d60522d470d24526394d642e6d5',
   # 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': '760dbed127dd9deb869f87e5924d851411cf15a2',
+  'quiche_revision': '98ad9db36b75a65f26f239a9aae68ecd1a330125',
   # 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.
@@ -357,7 +357,7 @@
   'ukey2_revision': '0275885d8e6038c39b8a8ca55e75d1d4d1727f47',
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'tint_revision': '7b7d69854d8d33c97b48795ec350d783dd09dd6a',
+  'tint_revision': 'c2118b0dcb2a11d93267dc0877584f9d6855e796',
 
   # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
@@ -554,7 +554,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '22cb5afa87d9fedc134ae64002e48a3c227b2798',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '25c9049d5a4ee13ed9085ca01a92ed3b25867989',
       'condition': 'checkout_ios',
   },
 
@@ -678,7 +678,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '1WBn0YPU9rlqli8Ctebdf9E3s-CIFp7jei5nojlCemAC',
+          'version': 'CUNayWpb4JErvQkdCk8cchP0sY4xV9vBv35PWhtW4bcC',
       },
     ],
     'condition': 'checkout_android',
@@ -882,6 +882,12 @@
       'dep_type': 'cipd',
   },
 
+  # Dependency for ChromeVox.
+  'src/third_party/chromevox/third_party/sre/src': {
+      'url': Var('chromium_git') + '/external/github.com/zorkow/speech-rule-engine.git' + '@' + '5a56d4d33d67dc7c692da032d2ebbdefd7de780e',
+      'condition': 'checkout_chromeos',
+  },
+
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
@@ -905,7 +911,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '399c5918bf47ff1fe8477f27b57fa0e8c67e438d',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b0763b2c57fed430291f0f2a76ec81976118d481',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1253,7 +1259,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'b98dcaa0db83132203774a577c0a64c39f7092e9',
+    Var('chromium_git') + '/openscreen' + '@' + '144746d4a6bd3df27095d321a97b01820c7c2e71',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '97cfe495bb7a3853266b646d1c79e169387f9c7a',
@@ -1270,7 +1276,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9b70f65e3d394d1294080c60bef32a484c1dfbd1',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c0182a50034c0c17a444c40aec96ee54239bc269',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1348,7 +1354,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '-TTr3It7eWfcBQBw8eIFSfLQo5tvHLBoyLYAb7f4LtcC'
+              'version': 'ze82f_wfveFwoydF7LkeHv0d-sI8F7BLasfX6xmLVM0C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1479,7 +1485,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + 'a6647318b57c0a05d590c8c21fc22aba87f08749',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@4dc6ccfad74a3ebb2623bceefff002243d595048',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@4ee2104e26ffe3402587f4013eaf5019e56a47c2',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'b1d65a2b3373fe12c622eaab65e5cf21b906d178',
@@ -1506,7 +1512,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '66460536ee975a3e98931b7b40a661a63fd9cd57',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'fa5ad8c0b5efb1b3ad90d8a726feb5cb221a04fe',
+    Var('webrtc_git') + '/src.git' + '@' + '8bf61a3071b7306860261eadf43c157ce3ffe4fe',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1554,7 +1560,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'Evb_hYwKX21F3mSBdnoPa1gPiJlejpLrfECXIpdrrqgC',
+          'version': 'xC87Xnbi3y7b2aSw2voaEk8MtTPrvU1peyj7NfC6ZtIC',
         },
       ],
       'dep_type': 'cipd',
@@ -1578,7 +1584,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3185e2343b7f9e915517d4125c95dbdb29e0d8f9',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@35f7c5f1cc0d0e3af064e520793179e35687438d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc b/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
index f5a3086..e73229c 100644
--- a/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -8,6 +8,14 @@
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace page_load_metrics {
+class PageLoadMetricsMemoryTracker;
+}  // namespace page_load_metrics
+
 namespace android_webview {
 
 namespace {
@@ -22,6 +30,9 @@
   bool IsNewTabPageUrl(const GURL& url) override;
   bool IsPrerender(content::WebContents* web_contents) override;
   bool IsExtensionUrl(const GURL& url) override;
+  page_load_metrics::PageLoadMetricsMemoryTracker*
+  GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override;
 
  protected:
   // page_load_metrics::PageLoadMetricsEmbedderBase:
@@ -58,6 +69,12 @@
   return false;
 }
 
+page_load_metrics::PageLoadMetricsMemoryTracker*
+PageLoadMetricsEmbedder::GetMemoryTrackerForBrowserContext(
+    content::BrowserContext* browser_context) {
+  return nullptr;
+}
+
 }  // namespace
 
 void InitializePageLoadMetricsForWebContents(
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 35cafd95..655058e8 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -726,6 +726,8 @@
     "power/hid_battery_util.h",
     "projector/projector_controller.cc",
     "projector/projector_controller.h",
+    "projector/projector_feature_pod_controller.cc",
+    "projector/projector_feature_pod_controller.h",
     "projector/projector_metadata_controller.cc",
     "projector/projector_metadata_controller.h",
     "projector/projector_metadata_model.cc",
@@ -1557,8 +1559,6 @@
     "wm/event_client_impl.h",
     "wm/full_restore/full_restore_controller.cc",
     "wm/full_restore/full_restore_controller.h",
-    "wm/full_restore/full_restore_window_manager.cc",
-    "wm/full_restore/full_restore_window_manager.h",
     "wm/fullscreen_window_finder.cc",
     "wm/fullscreen_window_finder.h",
     "wm/gestures/back_gesture/back_gesture_affordance.cc",
@@ -2239,7 +2239,6 @@
     "system/message_center/ash_message_popup_collection_unittest.cc",
     "system/message_center/inactive_user_notification_blocker_unittest.cc",
     "system/message_center/message_center_ui_controller_unittest.cc",
-    "system/message_center/message_center_utils_unittest.cc",
     "system/message_center/notification_swipe_control_view_unittest.cc",
     "system/message_center/notifier_settings_view_unittest.cc",
     "system/message_center/session_state_notification_blocker_unittest.cc",
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index da60e04..1d7e8dd4 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -744,8 +744,9 @@
           has_native_drag);
 }
 
-bool AppListFolderView::IsPointOutsideOfFolderBoundary(
-    const gfx::Point& point) {
+bool AppListFolderView::IsViewOutsideOfFolder(AppListItemView* view) {
+  gfx::Point point = view->GetLocalBounds().CenterPoint();
+  ConvertPointToTarget(view, this, &point);
   return !GetLocalBounds().Contains(point);
 }
 
diff --git a/ash/app_list/views/app_list_folder_view.h b/ash/app_list/views/app_list_folder_view.h
index 96c02ce..a0f8da2 100644
--- a/ash/app_list/views/app_list_folder_view.h
+++ b/ash/app_list/views/app_list_folder_view.h
@@ -148,7 +148,7 @@
       const gfx::Point& drag_point_in_folder_grid) override;
   void DispatchEndDragEventForReparent(bool events_forwarded_to_drag_drop_host,
                                        bool cancel_drag) override;
-  bool IsPointOutsideOfFolderBoundary(const gfx::Point& point) override;
+  bool IsViewOutsideOfFolder(AppListItemView* view) override;
   bool IsOEMFolder() const override;
   void SetRootLevelDragViewVisible(bool visible) override;
   void HandleKeyboardReparent(AppListItemView* reparented_view,
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index e0dc519c..3f408c8 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1825,10 +1825,8 @@
 
   // Calculate if the drag_view_ is dragged out of the folder's container
   // ink bubble.
-  gfx::Rect bounds_to_folder_view = ConvertRectToParent(drag_view_->bounds());
-  gfx::Point pt = bounds_to_folder_view.CenterPoint();
   bool is_item_dragged_out_of_folder =
-      folder_delegate_->IsPointOutsideOfFolderBoundary(pt);
+      folder_delegate_->IsViewOutsideOfFolder(drag_view_);
   if (is_item_dragged_out_of_folder) {
     if (!drag_out_of_folder_container_) {
       folder_item_reparent_timer_.Start(
diff --git a/ash/app_list/views/apps_grid_view_folder_delegate.h b/ash/app_list/views/apps_grid_view_folder_delegate.h
index 9853aa3..a398ca8 100644
--- a/ash/app_list/views/apps_grid_view_folder_delegate.h
+++ b/ash/app_list/views/apps_grid_view_folder_delegate.h
@@ -42,8 +42,9 @@
       bool events_forwarded_to_drag_drop_host,
       bool cancel_drag) = 0;
 
-  // Returns true if |point| falls outside of the folder container ink bubble.
-  virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point) = 0;
+  // Returns whether the |view| is within the folder view's bounds.
+  // The |view| is expected to be in the folder's apps grid view hierarchy.
+  virtual bool IsViewOutsideOfFolder(AppListItemView* view) = 0;
 
   // Returns true if the associated folder item is an OEM folder.
   virtual bool IsOEMFolder() const = 0;
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index e94e55e3..54d7d6e8 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -179,6 +179,36 @@
 
 }  // namespace
 
+class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
+ public:
+  TestAppsGridViewFolderDelegate() = default;
+  TestAppsGridViewFolderDelegate(const TestAppsGridViewFolderDelegate&) =
+      delete;
+  TestAppsGridViewFolderDelegate& operator=(
+      const TestAppsGridViewFolderDelegate&) = delete;
+  ~TestAppsGridViewFolderDelegate() override = default;
+
+  void ReparentItem(AppListItemView* original_drag_view,
+                    const gfx::Point& drag_point_in_folder_grid,
+                    bool has_native_drag) override {}
+
+  void DispatchDragEventForReparent(
+      AppsGridView::Pointer pointer,
+      const gfx::Point& drag_point_in_folder_grid) override {}
+
+  void DispatchEndDragEventForReparent(bool events_forwarded_to_drag_drop_host,
+                                       bool cancel_drag) override {}
+
+  bool IsViewOutsideOfFolder(AppListItemView* view) override { return false; }
+
+  bool IsOEMFolder() const override { return false; }
+
+  void SetRootLevelDragViewVisible(bool visible) override {}
+
+  void HandleKeyboardReparent(AppListItemView* reparented_item,
+                              ui::KeyboardCode key_code) override {}
+};
+
 class AppsGridViewTest : public views::ViewsTestBase,
                          public testing::WithParamInterface<bool> {
  public:
@@ -234,22 +264,40 @@
   }
 
  protected:
-  AppListItemView* GetItemViewAt(int index) const {
-    return static_cast<AppListItemView*>(test_api_->GetViewAtModelIndex(index));
+  AppListItemView* GetItemViewInAppsGridAt(int index,
+                                           AppsGridView* grid_view) const {
+    return grid_view->view_model()->view_at(index);
   }
 
-  AppListItemView* GetItemViewForPoint(const gfx::Point& point) const {
-    for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
-      AppListItemView* view = GetItemViewAt(i);
+  AppListItemView* GetItemViewInTopLevelGrid(int index) const {
+    return GetItemViewInAppsGridAt(index, apps_grid_view_);
+  }
+
+  AppListItemView* GetItemViewInAppsGridForPoint(
+      const gfx::Point& point,
+      AppsGridView* grid_view) const {
+    std::unique_ptr<AppsGridViewTestApi> temp_test_api =
+        std::make_unique<AppsGridViewTestApi>(grid_view);
+    AppListItemList* item_list_ = temp_test_api->GetItemList();
+    for (size_t i = 0; i < item_list_->item_count(); ++i) {
+      AppListItemView* view = GetItemViewInAppsGridAt(i, grid_view);
       gfx::Point view_origin = view->origin();
-      views::View::ConvertPointToTarget(view->parent(), apps_grid_view_,
-                                        &view_origin);
+      // Covert point to target if we are working with the app list's apps grid
+      // view.
+      if (grid_view == apps_grid_view_) {
+        views::View::ConvertPointToTarget(view->parent(), grid_view,
+                                          &view_origin);
+      }
       if (gfx::Rect(view_origin, view->size()).Contains(point))
         return view;
     }
     return nullptr;
   }
 
+  AppListItemView* GetItemViewForPoint(const gfx::Point& point) const {
+    return GetItemViewInAppsGridForPoint(point, apps_grid_view_);
+  }
+
   gfx::Rect GetItemRectOnCurrentPageAt(int row, int col) const {
     DCHECK_GT(model_->top_level_item_list()->item_count(), 0u);
     return test_api_->GetItemTileRectOnCurrentPageAt(row, col);
@@ -265,31 +313,69 @@
     return contents_view_->apps_container_view()->app_list_folder_view();
   }
 
+  AppsGridView* folder_apps_grid_view() const {
+    return app_list_folder_view()->items_grid_view();
+  }
+
+  AppListItemView* InitiateDrag(AppsGridView::Pointer pointer,
+                                const gfx::Point& from,
+                                AppsGridView* apps_grid_view) {
+    AppListItemView* view = GetItemViewInAppsGridForPoint(from, apps_grid_view);
+    DCHECK(view);
+    gfx::Point root_from(from);
+    gfx::NativeWindow window = app_list_view_->GetWidget()->GetNativeWindow();
+    views::View::ConvertPointToWidget(apps_grid_view, &root_from);
+    aura::Window::ConvertPointToTarget(window, window->GetRootWindow(),
+                                       &root_from);
+    // Ensure that the |root_from| point is correct if RTL.
+    root_from.set_x(apps_grid_view->GetMirroredXInView(root_from.x()));
+
+    apps_grid_view->InitiateDrag(view, pointer, root_from, root_from);
+    current_drag_location_ = root_from;
+    return view;
+  }
+
+  // Updates the drag from the current drag location to the destination point
+  // |to|. These coordinates are relative the |apps_grid_view| which may belong
+  // to either the app list or an open folder view.
+  void UpdateDrag(AppsGridView::Pointer pointer,
+                  const gfx::Point& to,
+                  AppsGridView* apps_grid_view,
+                  int steps = 1) {
+    // Check that the drag has been initialized.
+    DCHECK(current_drag_location_);
+    gfx::Point root_to(to);
+    gfx::NativeWindow window = apps_grid_view->GetWidget()->GetNativeWindow();
+    views::View::ConvertPointToWidget(apps_grid_view, &root_to);
+    aura::Window::ConvertPointToTarget(window, window->GetRootWindow(),
+                                       &root_to);
+    // Ensure that the |root_to| point is correct if RTL.
+    root_to.set_x(apps_grid_view->GetMirroredXInView(root_to.x()));
+
+    for (int step = 1; step <= steps; step += 1) {
+      gfx::Point drag_increment_point(*current_drag_location_);
+      drag_increment_point += gfx::Vector2d(
+          (root_to.x() - current_drag_location_->x()) * step / steps,
+          (root_to.y() - current_drag_location_->y()) * step / steps);
+      ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, to, drag_increment_point,
+                                ui::EventTimeForNow(), 0, 0);
+      apps_grid_view->UpdateDragFromItem(pointer, drag_event);
+    }
+
+    current_drag_location_ = root_to;
+  }
+
+  void EndDrag(AppsGridView* grid_view, bool cancel) {
+    grid_view->EndDrag(cancel);
+    current_drag_location_ = base::nullopt;
+  }
+
   // Points are in |apps_grid_view_|'s coordinates, and fixed for RTL.
   AppListItemView* SimulateDrag(AppsGridView::Pointer pointer,
                                 const gfx::Point& from,
                                 const gfx::Point& to) {
-    AppListItemView* view = GetItemViewForPoint(from);
-    DCHECK(view);
-
-    gfx::NativeWindow window = app_list_view_->GetWidget()->GetNativeWindow();
-    gfx::Point root_from(from);
-    views::View::ConvertPointToWidget(apps_grid_view_, &root_from);
-    aura::Window::ConvertPointToTarget(window, window->GetRootWindow(),
-                                       &root_from);
-    // Ensure that the |root_from| point is correct if RTL.
-    root_from.set_x(apps_grid_view_->GetMirroredXInView(root_from.x()));
-    gfx::Point root_to(to);
-    views::View::ConvertPointToWidget(apps_grid_view_, &root_to);
-    aura::Window::ConvertPointToTarget(window, window->GetRootWindow(),
-                                       &root_to);
-    // Ensure that the |root_to| point is correct if RTL.
-    root_to.set_x(apps_grid_view_->GetMirroredXInView(root_to.x()));
-    apps_grid_view_->InitiateDrag(view, pointer, from, root_from);
-
-    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, to, root_to,
-                              ui::EventTimeForNow(), 0, 0);
-    apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
+    AppListItemView* view = InitiateDrag(pointer, from, apps_grid_view_);
+    UpdateDrag(pointer, to, apps_grid_view_);
     return view;
   }
 
@@ -355,6 +441,8 @@
   bool create_as_tablet_mode_ = false;
 
  private:
+  base::Optional<gfx::Point> current_drag_location_;
+
   // Restores the locale to default when destructor is called.
   base::test::ScopedRestoreICUDefaultLocale restore_locale_;
 
@@ -366,37 +454,6 @@
 
 INSTANTIATE_TEST_SUITE_P(All, AppsGridViewTest, testing::Bool());
 
-class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
- public:
-  TestAppsGridViewFolderDelegate() = default;
-  ~TestAppsGridViewFolderDelegate() override = default;
-
-  void ReparentItem(AppListItemView* original_drag_view,
-                    const gfx::Point& drag_point_in_folder_grid,
-                    bool has_native_drag) override {}
-
-  void DispatchDragEventForReparent(
-      AppsGridView::Pointer pointer,
-      const gfx::Point& drag_point_in_folder_grid) override {}
-
-  void DispatchEndDragEventForReparent(bool events_forwarded_to_drag_drop_host,
-                                       bool cancel_drag) override {}
-
-  bool IsPointOutsideOfFolderBoundary(const gfx::Point& point) override {
-    return false;
-  }
-
-  bool IsOEMFolder() const override { return false; }
-
-  void SetRootLevelDragViewVisible(bool visible) override {}
-
-  void HandleKeyboardReparent(AppListItemView* reparented_item,
-                              ui::KeyboardCode key_code) override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
-};
-
 TEST_P(AppsGridViewTest, CreatePage) {
   // Fully populates a page.
   const int kPages = 1;
@@ -421,14 +478,14 @@
 
   model_->PopulateApps(kTotalItems);
 
-  AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
+  AppListItemView* last_view = GetItemViewInTopLevelGrid(kLastItemIndex);
   apps_grid_view_->SetSelectedView(last_view);
   model_->DeleteItem(model_->GetItemName(kLastItemIndex));
 
   EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
 
   // No crash happens.
-  AppListItemView* view = GetItemViewAt(0);
+  AppListItemView* view = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->SetSelectedView(view);
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
 }
@@ -440,8 +497,8 @@
   model_->PopulateApps(5);
 
   // Select the first app in grid and launch it.
-  contents_view_->GetAppListMainView()->ActivateApp(GetItemViewAt(0)->item(),
-                                                    0);
+  contents_view_->GetAppListMainView()->ActivateApp(
+      GetItemViewInTopLevelGrid(0)->item(), 0);
 
   // Test that histogram recorded app launch from grid.
   histogram_tester.ExpectBucketCount(
@@ -465,12 +522,12 @@
   ASSERT_LE(0, cols);
   model_->PopulateApps(cols * 2);
 
-  AppListItemView* view0 = GetItemViewAt(0);
+  AppListItemView* view0 = GetItemViewInTopLevelGrid(0);
   model_->top_level_item_list()->MoveItem(0, cols + 2);
 
   // Make sure the logical location of the view.
-  EXPECT_NE(view0, GetItemViewAt(0));
-  EXPECT_EQ(view0, GetItemViewAt(cols + 2));
+  EXPECT_NE(view0, GetItemViewInTopLevelGrid(0));
+  EXPECT_EQ(view0, GetItemViewInTopLevelGrid(cols + 2));
 
   // |view0| should be animating with layer.
   EXPECT_TRUE(view0->layer());
@@ -488,12 +545,12 @@
 
   apps_grid_view_->GetWidget()->Hide();
 
-  AppListItemView* view0 = GetItemViewAt(0);
+  AppListItemView* view0 = GetItemViewInTopLevelGrid(0);
   model_->top_level_item_list()->MoveItem(0, cols + 2);
 
   // Make sure the logical location of the view.
-  EXPECT_NE(view0, GetItemViewAt(0));
-  EXPECT_EQ(view0, GetItemViewAt(cols + 2));
+  EXPECT_NE(view0, GetItemViewInTopLevelGrid(0));
+  EXPECT_EQ(view0, GetItemViewInTopLevelGrid(cols + 2));
 
   // The item should be repositioned immediately when the widget is not visible.
   EXPECT_FALSE(view0->layer());
@@ -519,7 +576,7 @@
   AppListItem* item = model_->CreateAndAddItem("Item with short name");
   model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);
 
-  AppListItemView* item_view = GetItemViewAt(0);
+  AppListItemView* item_view = GetItemViewInTopLevelGrid(0);
   ASSERT_TRUE(item_view);
   const views::Label* title_label = item_view->title();
   EXPECT_EQ(base::ASCIIToUTF16(expected_tooltip),
@@ -534,7 +591,7 @@
   AppListItem* item = model_->CreateAndAddItem(title);
   model_->SetItemNameAndShortName(item, title, "");
 
-  AppListItemView* item_view = GetItemViewAt(0);
+  AppListItemView* item_view = GetItemViewInTopLevelGrid(0);
   ASSERT_TRUE(item_view);
   const views::Label* title_label = item_view->title();
   EXPECT_TRUE(
@@ -784,7 +841,7 @@
 TEST_F(AppsGridViewTest, AppIconSelectedWhenMenuIsShown) {
   model_->PopulateApps(1);
   ASSERT_EQ(1u, model_->top_level_item_list()->item_count());
-  AppListItemView* app = GetItemViewAt(0);
+  AppListItemView* app = GetItemViewInTopLevelGrid(0);
   EXPECT_FALSE(apps_grid_view_->IsSelectedView(app));
 
   // Send a mouse event which would show a context menu.
@@ -820,11 +877,11 @@
 
   std::vector<int> pages_to_check = {1, 0};
   for (int i : pages_to_check) {
-    apps_grid_view_->pagination_model()->SelectPage(i, /*animate=*/false);
+    GetPaginationModel()->SelectPage(i, /*animate=*/false);
 
     for (size_t j = 0; j < kItemsInPage; ++j) {
       const size_t idx = kItemsInPage * i + j;
-      AppListItemView* item_view = GetItemViewAt(idx);
+      AppListItemView* item_view = GetItemViewInTopLevelGrid(idx);
 
       // Send a mouse event which would show a context menu.
       ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, gfx::Point(),
@@ -860,7 +917,7 @@
   model_->PopulateApps(kTotalItems);
   // Normally individual item-view does not have a layer.
   for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i)
-    EXPECT_FALSE(GetItemViewAt(i)->layer());
+    EXPECT_FALSE(GetItemViewInTopLevelGrid(i)->layer());
   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());
 
@@ -871,13 +928,13 @@
   SimulateDrag(AppsGridView::MOUSE, from, to);
   // Each item view has its own layer during the drag.
   for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i)
-    EXPECT_TRUE(GetItemViewAt(i)->layer());
-  apps_grid_view_->EndDrag(false);
+    EXPECT_TRUE(GetItemViewInTopLevelGrid(i)->layer());
+  EndDrag(apps_grid_view_, false /*cancel*/);
 
   // The layer should be destroyed after the dragging.
   test_api_->WaitForItemMoveAnimationDone();
   for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i)
-    EXPECT_FALSE(GetItemViewAt(i)->layer());
+    EXPECT_FALSE(GetItemViewInTopLevelGrid(i)->layer());
   EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
   EXPECT_EQ(AppListFolderItem::kItemType,
             model_->top_level_item_list()->item_at(0)->GetItemType());
@@ -896,7 +953,7 @@
 
   // Dragging item_2 to the folder adds item_2 to the folder.
   SimulateDrag(AppsGridView::MOUSE, from, to);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
 
   EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
   EXPECT_EQ(folder_item->id(), model_->GetModelContent());
@@ -913,6 +970,141 @@
   test_api_->LayoutToIdealBounds();
 }
 
+TEST_P(AppsGridViewTest, MouseDragItemOutOfFolderFirstPage) {
+  // Creates a folder item.
+  const size_t kTotalItems =
+      AppListConfig::instance().max_folder_items_per_page();
+  AppListFolderItem* folder_item =
+      model_->CreateAndPopulateFolderWithApps(kTotalItems);
+  EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
+  EXPECT_EQ(AppListFolderItem::kItemType,
+            model_->top_level_item_list()->item_at(0)->GetItemType());
+  EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
+
+  // Open the folder and check it's contents.
+  test_api_->Update();
+  test_api_->PressItemAt(0);
+  EXPECT_EQ(4, folder_apps_grid_view()->cols());
+  EXPECT_EQ(4, folder_apps_grid_view()->rows_per_page());
+  ash::PaginationModel* folder_pagination_model =
+      folder_apps_grid_view()->pagination_model();
+  EXPECT_EQ(1, folder_pagination_model->total_pages());
+  EXPECT_EQ(0, folder_pagination_model->selected_page());
+  EXPECT_TRUE(folder_apps_grid_view()->is_in_folder());
+
+  AppsGridViewTestApi folder_grid_test_api(folder_apps_grid_view());
+
+  gfx::Point from =
+      folder_grid_test_api.GetItemTileRectOnCurrentPageAt(0, 0).CenterPoint();
+  // Drag the first folder child out of the folder.
+  AppListItemView* drag_view =
+      InitiateDrag(AppsGridView::MOUSE, from, folder_apps_grid_view());
+  // Calculate the target destination for our drag and update the drag to that
+  // location.
+  gfx::Point empty_space =
+      app_list_folder_view()->GetLocalBounds().bottom_center() +
+      gfx::Vector2d(0, drag_view->height()
+                    /*padding to completely exit folder view*/);
+
+  UpdateDrag(AppsGridView::MOUSE, empty_space, folder_apps_grid_view(),
+             10 /*steps*/);
+
+  // Fire the reparent timer that should be started when an item is dragged out
+  // of folder bounds.
+  ASSERT_TRUE(folder_apps_grid_view()->FireFolderItemReparentTimerForTest());
+  // Calculate the coordinates for the drop point. Note that we we are dropping
+  // into the app list view not the folder view. The (0,1) spot is empty.
+  gfx::Point drop_point = GetItemRectOnCurrentPageAt(0, 1).CenterPoint();
+  drop_point.set_x(apps_grid_view_->GetMirroredXInView(drop_point.x()));
+  views::View::ConvertPointToTarget(apps_grid_view_, folder_apps_grid_view(),
+                                    &drop_point);
+  UpdateDrag(AppsGridView::MOUSE, drop_point, folder_apps_grid_view(),
+             5 /*steps*/);
+
+  // End the drag and assert that the item has been dragged out of the folder
+  // and the app list's grid view has been updated accordingly.
+  EndDrag(folder_apps_grid_view(), false /*cancel*/);
+  AppListItem* item_0 = model_->FindItem("Item 0");
+  AppListItem* item_1 = model_->FindItem("Item 1");
+  EXPECT_FALSE(item_0->IsInFolder());
+  EXPECT_TRUE(item_1->IsInFolder());
+  EXPECT_EQ(folder_item->id(), item_1->folder_id());
+  EXPECT_EQ(std::string(folder_item->id() + ",Item 0"),
+            model_->GetModelContent());
+  EXPECT_EQ(kTotalItems - 1, folder_item->ChildItemCount());
+}
+
+TEST_P(AppsGridViewTest, MouseDragItemOutOfFolderSecondPage) {
+  // Creates a folder item with enough views to have a second page.
+  const size_t kTotalItems =
+      AppListConfig::instance().max_folder_items_per_page() + 1;
+  AppListFolderItem* folder_item =
+      model_->CreateAndPopulateFolderWithApps(kTotalItems);
+  EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
+  EXPECT_EQ(AppListFolderItem::kItemType,
+            model_->top_level_item_list()->item_at(0)->GetItemType());
+  EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
+
+  // Open the folder and check it's contents.
+  test_api_->Update();
+  test_api_->PressItemAt(0);
+  EXPECT_EQ(4, folder_apps_grid_view()->cols());
+  EXPECT_EQ(4, folder_apps_grid_view()->rows_per_page());
+  ash::PaginationModel* folder_pagination_model =
+      folder_apps_grid_view()->pagination_model();
+  EXPECT_EQ(2, folder_pagination_model->total_pages());
+  EXPECT_EQ(0, folder_pagination_model->selected_page());
+  EXPECT_TRUE(folder_apps_grid_view()->is_in_folder());
+
+  // Switch to second page and check it's contents.
+  folder_pagination_model->SelectPage(1, false /*animate*/);
+  EXPECT_EQ(1, folder_pagination_model->selected_page());
+  EXPECT_EQ(4, folder_apps_grid_view()->cols());
+  EXPECT_EQ(4, folder_apps_grid_view()->rows_per_page());
+  EXPECT_TRUE(folder_apps_grid_view()->is_in_folder());
+
+  AppsGridViewTestApi folder_grid_test_api(folder_apps_grid_view());
+
+  gfx::Point from =
+      folder_grid_test_api.GetItemTileRectOnCurrentPageAt(0, 0).CenterPoint();
+  // Drag the first folder child on the second page out of the folder.
+  AppListItemView* drag_view =
+      InitiateDrag(AppsGridView::MOUSE, from, folder_apps_grid_view());
+  // Calculate the target destination for our drag and update the drag to that
+  // location.
+  gfx::Point empty_space =
+      app_list_folder_view()->GetLocalBounds().bottom_center() +
+      gfx::Vector2d(0, drag_view->height()
+                    /*padding to completely exit folder view*/);
+
+  UpdateDrag(AppsGridView::MOUSE, empty_space, folder_apps_grid_view(),
+             10 /*steps*/);
+
+  // Fire the reparent timer that should be started when an item is dragged out
+  // of folder bounds.
+  ASSERT_TRUE(folder_apps_grid_view()->FireFolderItemReparentTimerForTest());
+  // Calculate the coordinates for the drop point. Note that we we are dropping
+  // into the app list view not the folder view. The (0,1) spot is empty.
+  gfx::Point drop_point = GetItemRectOnCurrentPageAt(0, 1).CenterPoint();
+  drop_point.set_x(apps_grid_view_->GetMirroredXInView(drop_point.x()));
+  views::View::ConvertPointToTarget(apps_grid_view_, folder_apps_grid_view(),
+                                    &drop_point);
+  UpdateDrag(AppsGridView::MOUSE, drop_point, folder_apps_grid_view(),
+             5 /*steps*/);
+
+  // End the drag and assert that the item has been dragged out of the folder
+  // and the app list's grid view has been updated accordingly.
+  EndDrag(folder_apps_grid_view(), false /*cancel*/);
+  AppListItem* item_0 = model_->FindItem("Item 0");
+  AppListItem* item_1 = model_->FindItem("Item 1");
+  EXPECT_FALSE(item_0->IsInFolder());
+  EXPECT_TRUE(item_1->IsInFolder());
+  EXPECT_EQ(folder_item->id(), item_1->folder_id());
+  EXPECT_EQ(std::string(folder_item->id() + ",Item 0"),
+            model_->GetModelContent());
+  EXPECT_EQ(kTotalItems - 1, folder_item->ChildItemCount());
+}
+
 TEST_P(AppsGridViewTest, MouseDragMaxItemsInFolder) {
   // Create and add a folder with |kMaxFolderItemsFullscreen - 1| items.
   const size_t kMaxItems =
@@ -941,7 +1133,7 @@
 
   // Dragging one item into the folder, the folder should accept the item.
   SimulateDrag(AppsGridView::MOUSE, from, to);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
   EXPECT_EQ(kMaxItems, folder_item->ChildItemCount());
@@ -952,7 +1144,7 @@
   // Dragging the last item over the folder, the folder won't accept the new
   // item.
   SimulateDrag(AppsGridView::MOUSE, from, to);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
   EXPECT_EQ(kMaxItems, folder_item->ChildItemCount());
   test_api_->LayoutToIdealBounds();
@@ -1004,7 +1196,7 @@
   ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, to,
                             ui::EventTimeForNow(), 0, 0);
   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
 
   // The item should not have moved into the folder.
   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
@@ -1035,7 +1227,7 @@
   // Drag left but stop before the folder dropping circle.
   drag_vector.set_x(-half_tile_width - 4);
   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3,Item 4,Item 5,Item 6"),
             model_->GetModelContent());
   TestAppListItemViewIndice();
@@ -1047,7 +1239,7 @@
                     4);
   SimulateDrag(AppsGridView::MOUSE, top_right + last_drag_vector,
                top_right + drag_vector);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3,Item 4,Item 5,Item 6"),
             model_->GetModelContent());
   TestAppListItemViewIndice();
@@ -1059,7 +1251,7 @@
   drag_vector.set_y(tile_height);
   SimulateDrag(AppsGridView::MOUSE, top_right + last_drag_vector,
                top_right + drag_vector);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 2,Item 3,Item 4,Item 5,Item 1,Item 6"),
             model_->GetModelContent());
   TestAppListItemViewIndice();
@@ -1071,7 +1263,7 @@
   drag_vector.set_y(0);
   SimulateDrag(AppsGridView::MOUSE, top_right + last_drag_vector,
                top_right + drag_vector);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3,Item 4,Item 5,Item 6"),
             model_->GetModelContent());
   TestAppListItemViewIndice();
@@ -1082,7 +1274,7 @@
   drag_vector.set_y(2 * tile_height);
   SimulateDrag(AppsGridView::MOUSE, top_right + last_drag_vector,
                top_right + drag_vector);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 2,Item 3,Item 4,Item 5,Item 6,Item 1"),
             model_->GetModelContent());
   TestAppListItemViewIndice();
@@ -1105,7 +1297,7 @@
   // Dragging folder over item_1 should leads to re-ordering these two
   // items.
   SimulateDrag(AppsGridView::MOUSE, from, to);
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
@@ -1125,7 +1317,7 @@
 
   // Canceling drag should keep existing order.
   SimulateDrag(AppsGridView::MOUSE, from, to);
-  apps_grid_view_->EndDrag(true);
+  EndDrag(apps_grid_view_, true /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
             model_->GetModelContent());
   test_api_->LayoutToIdealBounds();
@@ -1133,14 +1325,14 @@
   // Deleting an item keeps remaining intact.
   SimulateDrag(AppsGridView::MOUSE, from, to);
   model_->DeleteItem(model_->GetItemName(2));
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
   test_api_->LayoutToIdealBounds();
 
   // Adding a launcher item cancels the drag and respects the order.
   SimulateDrag(AppsGridView::MOUSE, from, to);
   model_->CreateAndAddItem("Extra");
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
             model_->GetModelContent());
   test_api_->LayoutToIdealBounds();
@@ -1150,7 +1342,7 @@
 TEST_F(AppsGridViewTest, ControlArrowSwapsAppsWithinSamePage) {
   model_->PopulateApps(GetTilesPerPage(0));
 
-  AppListItemView* moving_item = GetItemViewAt(0);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Test that moving left from 0,0 does not move the app.
@@ -1162,39 +1354,41 @@
   // Test that moving up from 0,0 does not move the app.
   SimulateKeyPress(ui::VKEY_UP, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(0));
+  EXPECT_EQ(moving_item, GetItemViewInTopLevelGrid(0));
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(moving_item));
 
   // Test that moving right from 0,0 results in a swap with the item adjacent.
-  AppListItemView* swapped_item = GetItemViewAt(1);
+  AppListItemView* swapped_item = GetItemViewInTopLevelGrid(1);
   SimulateKeyPress(ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(1));
-  EXPECT_EQ(swapped_item, GetItemViewAt(0));
+  EXPECT_EQ(moving_item, GetItemViewInTopLevelGrid(1));
+  EXPECT_EQ(swapped_item, GetItemViewInTopLevelGrid(0));
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(moving_item));
 
   // Test that moving down from 0,1 results in a swap with the item at 1,1.
-  swapped_item = GetItemViewAt(apps_grid_view_->cols() + 1);
+  swapped_item = GetItemViewInTopLevelGrid(apps_grid_view_->cols() + 1);
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(apps_grid_view_->cols() + 1));
-  EXPECT_EQ(swapped_item, GetItemViewAt(1));
+  EXPECT_EQ(moving_item,
+            GetItemViewInTopLevelGrid(apps_grid_view_->cols() + 1));
+  EXPECT_EQ(swapped_item, GetItemViewInTopLevelGrid(1));
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(moving_item));
 
   // Test that moving left from 1,1 results in a swap with the item at 1,0.
-  swapped_item = GetItemViewAt(apps_grid_view_->cols());
+  swapped_item = GetItemViewInTopLevelGrid(apps_grid_view_->cols());
   SimulateKeyPress(ui::VKEY_LEFT, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(apps_grid_view_->cols()));
-  EXPECT_EQ(swapped_item, GetItemViewAt(apps_grid_view_->cols() + 1));
+  EXPECT_EQ(moving_item, GetItemViewInTopLevelGrid(apps_grid_view_->cols()));
+  EXPECT_EQ(swapped_item,
+            GetItemViewInTopLevelGrid(apps_grid_view_->cols() + 1));
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(moving_item));
 
   // Test that moving up from 1,0 results in a swap with the item at 0,0.
-  swapped_item = GetItemViewAt(0);
+  swapped_item = GetItemViewInTopLevelGrid(0);
   SimulateKeyPress(ui::VKEY_UP, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(0));
-  EXPECT_EQ(swapped_item, GetItemViewAt(apps_grid_view_->cols()));
+  EXPECT_EQ(moving_item, GetItemViewInTopLevelGrid(0));
+  EXPECT_EQ(swapped_item, GetItemViewInTopLevelGrid(apps_grid_view_->cols()));
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(moving_item));
 }
 
@@ -1203,7 +1397,7 @@
   base::HistogramTester histogram_tester;
   model_->PopulateApps(GetTilesPerPage(0));
 
-  AppListItemView* moving_item = GetItemViewAt(0);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Make one move right and expect a histogram is recorded.
@@ -1236,7 +1430,7 @@
   base::HistogramTester histogram_tester;
   model_->PopulateApps(GetTilesPerPage(0));
 
-  AppListItemView* moving_item = GetItemViewAt(0);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Make 2 no-op moves and one successful move from 0,0 ane expect a histogram
@@ -1258,7 +1452,7 @@
   base::HistogramTester histogram_tester;
   model_->PopulateApps(GetTilesPerPage(1) * 2);
 
-  AppListItemView* moving_item = GetItemViewAt(0);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Make that a series of moves when the control key is left pressed and expect
@@ -1279,16 +1473,19 @@
   model_->PopulateApps(apps_grid_view_->cols() + 1);
 
   // Select the far right item.
-  AppListItemView* moving_item = GetItemViewAt(apps_grid_view_->cols() - 1);
-  AppListItemView* swapped_item = GetItemViewAt(apps_grid_view_->cols());
+  AppListItemView* moving_item =
+      GetItemViewInTopLevelGrid(apps_grid_view_->cols() - 1);
+  AppListItemView* swapped_item =
+      GetItemViewInTopLevelGrid(apps_grid_view_->cols());
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Press down to move the app to the next row. It should take the place of the
   // app on the next row that is closes to the column of |moving_item|.
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
 
-  EXPECT_EQ(moving_item, GetItemViewAt(apps_grid_view_->cols()));
-  EXPECT_EQ(swapped_item, GetItemViewAt(apps_grid_view_->cols() - 1));
+  EXPECT_EQ(moving_item, GetItemViewInTopLevelGrid(apps_grid_view_->cols()));
+  EXPECT_EQ(swapped_item,
+            GetItemViewInTopLevelGrid(apps_grid_view_->cols() - 1));
 }
 
 // Tests that moving an app up/down/left/right to a full page results in the app
@@ -1382,7 +1579,8 @@
   model_->PopulateApps(kTilesPerPageStart);
 
   // Focus the last item on the page.
-  AppListItemView* moving_item = GetItemViewAt(kTilesPerPageStart - 1);
+  AppListItemView* moving_item =
+      GetItemViewInTopLevelGrid(kTilesPerPageStart - 1);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   // Test that pressing control-right creates a new page.
@@ -1422,7 +1620,8 @@
   base::HistogramTester histogram_tester;
   // Move an app so it is by itself on page 1.
   model_->PopulateApps(GetTilesPerPage(0));
-  AppListItemView* moving_item = GetItemViewAt(GetTilesPerPage(0) - 1);
+  AppListItemView* moving_item =
+      GetItemViewInTopLevelGrid(GetTilesPerPage(0) - 1);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
   histogram_tester.ExpectBucketCount("Apps.AppListPageSwitcherSource", 7, 1);
@@ -1456,7 +1655,8 @@
   base::HistogramTester histogram_tester;
   // Move an app so it is by itself on page 1.
   model_->PopulateApps(GetTilesPerPage(0));
-  AppListItemView* moving_item = GetItemViewAt(GetTilesPerPage(0) - 1);
+  AppListItemView* moving_item =
+      GetItemViewInTopLevelGrid(GetTilesPerPage(0) - 1);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
   SimulateKeyReleased(ui::VKEY_DOWN, ui::EF_NONE);
@@ -1491,7 +1691,8 @@
 TEST_F(AppsGridViewTest, ControlArrowDownOrRightRemovesPage) {
   // Move an app so it is by itself on page 1, with another app on page 2.
   model_->PopulateApps(GetTilesPerPage(0));
-  AppListItemView* moving_item = GetItemViewAt(GetTilesPerPage(0) - 1);
+  AppListItemView* moving_item =
+      GetItemViewInTopLevelGrid(GetTilesPerPage(0) - 1);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
   SimulateKeyPress(ui::VKEY_UP);
@@ -1532,16 +1733,16 @@
   base::HistogramTester histogram_tester;
   model_->PopulateApps(GetTilesPerPage(0));
   // Select the first item in the grid, folder it with the item to the right.
-  AppListItemView* first_item = GetItemViewAt(0);
+  AppListItemView* first_item = GetItemViewInTopLevelGrid(0);
   const std::string first_item_id = first_item->item()->id();
-  const std::string second_item_id = GetItemViewAt(1)->item()->id();
+  const std::string second_item_id = GetItemViewInTopLevelGrid(1)->item()->id();
   apps_grid_view_->GetFocusManager()->SetFocusedView(first_item);
 
   SimulateKeyPress(ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
 
   // Test that the first item in the grid is now a folder with the first and
   // second items, and that the folder is the selected view.
-  AppListItemView* new_folder = GetItemViewAt(0);
+  AppListItemView* new_folder = GetItemViewInTopLevelGrid(0);
   ASSERT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   AppListFolderItem* folder_item =
       static_cast<AppListFolderItem*>(new_folder->item());
@@ -1562,7 +1763,8 @@
 
   // Move selection to the item to the right of the folder and put it in the
   // folder.
-  apps_grid_view_->GetFocusManager()->SetFocusedView(GetItemViewAt(1));
+  apps_grid_view_->GetFocusManager()->SetFocusedView(
+      GetItemViewInTopLevelGrid(1));
 
   SimulateKeyPress(ui::VKEY_LEFT, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
 
@@ -1657,17 +1859,17 @@
   model_->PopulateApps(kNumberOfApps);
   // Select the second to last item in the grid, folder it with the item to the
   // right.
-  AppListItemView* moving_item = GetItemViewAt(kNumberOfApps - 2);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(kNumberOfApps - 2);
   const std::string first_item_id = moving_item->item()->id();
   const std::string second_item_id =
-      GetItemViewAt(kNumberOfApps - 1)->item()->id();
+      GetItemViewInTopLevelGrid(kNumberOfApps - 1)->item()->id();
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
 
   SimulateKeyPress(ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
 
   // Test that the first item in the grid is now a folder with the first and
   // second items, and that the folder is the selected view.
-  AppListItemView* new_folder = GetItemViewAt(kNumberOfApps - 2);
+  AppListItemView* new_folder = GetItemViewInTopLevelGrid(kNumberOfApps - 2);
   ASSERT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   AppListFolderItem* folder_item =
       static_cast<AppListFolderItem*>(new_folder->item());
@@ -1715,7 +1917,7 @@
 
   // Cancel drag and put the dragged view back to its ideal position so that
   // the next drag would pick it up.
-  apps_grid_view_->EndDrag(true);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   test_api_->LayoutToIdealBounds();
 
   // Now drag to the top edge, and test the other direction.
@@ -1734,7 +1936,7 @@
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   EXPECT_EQ(to, GetDragViewCenter());
 
-  apps_grid_view_->EndDrag(true);
+  EndDrag(apps_grid_view_, false /*cancel*/);
 }
 
 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
@@ -1750,7 +1952,7 @@
 
   // Starts a mouse drag and then cancels it.
   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
-  apps_grid_view_->EndDrag(true);
+  EndDrag(apps_grid_view_, true /*cancel*/);
   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
             model_->GetModelContent());
 }
@@ -1772,7 +1974,7 @@
   EXPECT_FALSE(search_box->HasFocus());
   EXPECT_TRUE(item_view->HasFocus());
 
-  apps_grid_view_->EndDrag(false);
+  EndDrag(apps_grid_view_, false /*cancel*/);
   EXPECT_FALSE(search_box->HasFocus());
   EXPECT_TRUE(item_view->HasFocus());
 }
@@ -1861,7 +2063,7 @@
       apps_grid_view_origin.x(), apps_grid_view_origin.y(), 0,
       base::TimeTicks(), ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
 
-  AppListItemView* folder_view = GetItemViewAt(0);
+  AppListItemView* folder_view = GetItemViewInTopLevelGrid(0);
   ASSERT_TRUE(folder_view->is_folder());
   ASSERT_FALSE(apps_grid_view_->layer()->layer_mask_layer());
 
@@ -1946,7 +2148,7 @@
     while (test_api_->HasPendingPageFlip()) {
       page_flip_waiter_->Wait();
     }
-    apps_grid_view_->EndDrag(false);
+    EndDrag(apps_grid_view_, false /*cancel*/);
     test_api_->LayoutToIdealBounds();
   }
 
@@ -2218,12 +2420,12 @@
   model_->PopulateApps(2);
 
   // Select first app and move it with the keyboard down to create a new page.
-  AppListItemView* moving_item = GetItemViewAt(0);
+  AppListItemView* moving_item = GetItemViewInTopLevelGrid(0);
   apps_grid_view_->GetFocusManager()->SetFocusedView(moving_item);
   SimulateKeyPress(ui::VKEY_DOWN, ui::EF_CONTROL_DOWN);
   SimulateKeyReleased(ui::VKEY_DOWN, ui::EF_NONE);
 
-  ASSERT_EQ(apps_grid_view_->pagination_model()->total_pages(), 2);
+  ASSERT_EQ(GetPaginationModel()->total_pages(), 2);
   histogram_tester.ExpectBucketCount(
       "Apps.AppList.AppsGridAddPage",
       AppListPageCreationType::kMovingAppWithKeyboard, 1);
@@ -2249,9 +2451,9 @@
   while (test_api_->HasPendingPageFlip())
     page_flip_waiter.Wait();
 
-  apps_grid_view_->EndDrag(false /*cancel*/);
+  EndDrag(apps_grid_view_, false /*cancel*/);
 
-  ASSERT_EQ(apps_grid_view_->pagination_model()->total_pages(), 2);
+  ASSERT_EQ(GetPaginationModel()->total_pages(), 2);
   histogram_tester.ExpectBucketCount("Apps.AppList.AppsGridAddPage",
                                      AppListPageCreationType::kDraggingApp, 1);
 }
@@ -2264,7 +2466,7 @@
   // recorded.
   model_->CreateAndAddItem("Extra App");
 
-  ASSERT_EQ(apps_grid_view_->pagination_model()->total_pages(), 2);
+  ASSERT_EQ(GetPaginationModel()->total_pages(), 2);
   histogram_tester.ExpectBucketCount("Apps.AppList.AppsGridAddPage",
                                      AppListPageCreationType::kSyncOrInstall,
                                      1);
diff --git a/ash/app_list/views/test/apps_grid_view_test_api.h b/ash/app_list/views/test/apps_grid_view_test_api.h
index 95e2380..cf20cfa9 100644
--- a/ash/app_list/views/test/apps_grid_view_test_api.h
+++ b/ash/app_list/views/test/apps_grid_view_test_api.h
@@ -50,6 +50,10 @@
 
   void WaitForItemMoveAnimationDone();
 
+  void Update() { view_->Update(); }
+
+  AppListItemList* GetItemList() { return view_->item_list_; }
+
  private:
   AppsGridView* view_;
 
diff --git a/ash/app_menu/notification_item_view.cc b/ash/app_menu/notification_item_view.cc
index f352705..0697dd0 100644
--- a/ash/app_menu/notification_item_view.cc
+++ b/ash/app_menu/notification_item_view.cc
@@ -111,9 +111,9 @@
 }
 
 gfx::Size NotificationItemView::CalculatePreferredSize() const {
-  return gfx::Size(
-      views::MenuConfig::instance().touchable_menu_width - kBorderStrokeWidth,
-      kNotificationItemViewHeight);
+  return gfx::Size(views::MenuConfig::instance().touchable_menu_min_width -
+                       kBorderStrokeWidth,
+                   kNotificationItemViewHeight);
 }
 
 void NotificationItemView::Layout() {
@@ -122,7 +122,7 @@
   // result of |text_container_| being too small to hold the full width of its
   // children labels.
   const gfx::Size text_container_size(
-      views::MenuConfig::instance().touchable_menu_width -
+      views::MenuConfig::instance().touchable_menu_min_width -
           kNotificationHorizontalPadding - kIconHorizontalPadding * 2 -
           kProportionalIconViewSize.width(),
       title_label_->GetPreferredSize().height() +
diff --git a/ash/app_menu/notification_menu_header_view.cc b/ash/app_menu/notification_menu_header_view.cc
index e7a79c7f..ed30b80 100644
--- a/ash/app_menu/notification_menu_header_view.cc
+++ b/ash/app_menu/notification_menu_header_view.cc
@@ -58,7 +58,7 @@
 
 gfx::Size NotificationMenuHeaderView::CalculatePreferredSize() const {
   return gfx::Size(
-      views::MenuConfig::instance().touchable_menu_width,
+      views::MenuConfig::instance().touchable_menu_min_width,
       GetInsets().height() + notification_title_->GetPreferredSize().height());
 }
 
diff --git a/ash/app_menu/notification_menu_view.cc b/ash/app_menu/notification_menu_view.cc
index 5d4cbf7..ab166c4 100644
--- a/ash/app_menu/notification_menu_view.cc
+++ b/ash/app_menu/notification_menu_view.cc
@@ -41,7 +41,7 @@
 
 gfx::Size NotificationMenuView::CalculatePreferredSize() const {
   return gfx::Size(
-      views::MenuConfig::instance().touchable_menu_width,
+      views::MenuConfig::instance().touchable_menu_min_width,
       double_separator_->GetPreferredSize().height() +
           header_view_->GetPreferredSize().height() +
           kNotificationItemViewHeight +
@@ -50,10 +50,10 @@
 
 void NotificationMenuView::Layout() {
   int y = 0;
-  double_separator_->SetBoundsRect(
-      gfx::Rect(gfx::Point(0, y),
-                gfx::Size(views::MenuConfig::instance().touchable_menu_width,
-                          double_separator_->GetPreferredSize().height())));
+  double_separator_->SetBoundsRect(gfx::Rect(
+      gfx::Point(0, y),
+      gfx::Size(views::MenuConfig::instance().touchable_menu_min_width,
+                double_separator_->GetPreferredSize().height())));
   y += double_separator_->GetPreferredSize().height();
 
   header_view_->SetBoundsRect(
diff --git a/ash/app_menu/notification_overflow_view.cc b/ash/app_menu/notification_overflow_view.cc
index a7d877a..6c28d88 100644
--- a/ash/app_menu/notification_overflow_view.cc
+++ b/ash/app_menu/notification_overflow_view.cc
@@ -138,7 +138,7 @@
   // padding on the bottom due to the corner radius of the root MenuItemView. If
   // the corner radius changes, |kOverflowSeparatorToIconPadding| must be
   // modified to vertically center the overflow icons.
-  return gfx::Size(views::MenuConfig::instance().touchable_menu_width,
+  return gfx::Size(views::MenuConfig::instance().touchable_menu_min_width,
                    separator_->GetPreferredSize().height() +
                        kOverflowSeparatorToIconPadding + kIconLayoutSize);
 }
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index c4fa2c2..49ef314 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -433,9 +433,6 @@
          =1 {1 notification}
          other {# notifications}}
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_HIDDEN_NOTIFICATION_COUNT_LABEL" desc="The label for the number of hidden notifications.">
-        +<ph name="COUNT">$1<ex>2</ex></ph>
-      </message>
 
       <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH" desc="The label used as the header in the bluetooth popup. [CHAR_LIMIT=14]">
         Bluetooth
@@ -1232,6 +1229,9 @@
       <message name="IDS_ASH_PHONE_HUB_ENABLE_HOTSPOT_NO_RECEPTION_STATE_TOOLTIP" desc="Tooltip message that indicates to the user that the Enable Hotspot feature is disabled because their phone does not have mobile data.">
         Your phone must have mobile data to provide a hotspot
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_PROJECTOR_BUTTON_LABEL" desc="The label text for the button in the status tray to Projector.">
+        Presentation tools
+      </message>
       <message name="IDS_ASH_PROJECTOR_KEY_IDEA_MARKED" desc="Message content on the toast that appears when a key idea is marked">
         Marked as a key idea
       </message>
@@ -3254,7 +3254,7 @@
       <message name="IDS_ASH_SCREEN_CAPTURE_BUTTON_EDIT" desc="The edit button label for the screen capture notification.">
         Edit
       </message>
-      <message name="IDS_ASH_SCREEN_CAPTURE_BUTTON_DELETE" desc="The delete button label for the screen capture notificaiton.">
+      <message name="IDS_ASH_SCREEN_CAPTURE_BUTTON_DELETE" desc="The delete button label for the screen capture notification.">
         Delete
       </message>
       <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_FULLSCREEN_IMAGE_CAPTURE_CLAMSHELL" desc="The capture label message which shows in the middle of the screen when in fullscreen image capture mode in clamshell mode.">
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_HIDDEN_NOTIFICATION_COUNT_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_HIDDEN_NOTIFICATION_COUNT_LABEL.png.sha1
deleted file mode 100644
index 55fb7c01fa..0000000
--- a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_HIDDEN_NOTIFICATION_COUNT_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3e74c9c2d78c09c2255dd22e5c25c8cd44964642
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_PROJECTOR_BUTTON_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_PROJECTOR_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..1c94037c
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_PROJECTOR_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+f0c469fa8e0e4e2ef41cdbda3f31cb47a482f8af
\ No newline at end of file
diff --git a/ash/assistant/assistant_controller_impl.cc b/ash/assistant/assistant_controller_impl.cc
index a4ff0ac..ff6c463 100644
--- a/ash/assistant/assistant_controller_impl.cc
+++ b/ash/assistant/assistant_controller_impl.cc
@@ -88,7 +88,8 @@
   assistant_feedback.assistant_debug_info_allowed =
       assistant_debug_info_allowed;
   assistant_feedback.description = feedback_description;
-  assistant_feedback.screenshot_png = screenshot_png;
+  assistant_feedback.screenshot_png =
+      std::vector<uint8_t>(screenshot_png.begin(), screenshot_png.end());
   assistant_->SendAssistantFeedback(std::move(assistant_feedback));
 }
 
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc
index 1873b25da..ace01d90 100644
--- a/ash/clipboard/views/clipboard_history_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -263,7 +263,7 @@
 
 gfx::Size ClipboardHistoryItemView::CalculatePreferredSize() const {
   const int preferred_width =
-      views::MenuConfig::instance().touchable_menu_width;
+      views::MenuConfig::instance().touchable_menu_min_width;
   return gfx::Size(preferred_width, GetHeightForWidth(preferred_width));
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 8054648..f0163f2 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -296,6 +296,10 @@
 const base::Feature kEnableOobeChromeVoxHint{"EnableOobeChromeVoxHint",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables toggling Pciguard settings through Settings UI.
+const base::Feature kEnablePciguardUi{"EnablePciguardUi",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables Device End Of Lifetime warning notifications.
 const base::Feature kEolWarningNotifications{"EolWarningNotifications",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
@@ -548,6 +552,10 @@
 // Controls whether to enable projector.
 const base::Feature kProjector{"Projector", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether to enable projector in status tray.
+const base::Feature kProjectorFeaturePod{"ProjectorFeaturePod",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to enable quick answers.
 const base::Feature kQuickAnswers{"QuickAnswers",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
@@ -831,6 +839,10 @@
   return base::FeatureList::IsEnabled(kImprovedKeyboardShortcuts);
 }
 
+bool IsPciguardUiEnabled() {
+  return base::FeatureList::IsEnabled(kEnablePciguardUi);
+}
+
 bool IsPhoneHubEnabled() {
   return base::FeatureList::IsEnabled(kPhoneHub);
 }
@@ -851,6 +863,11 @@
   return base::FeatureList::IsEnabled(kProjector);
 }
 
+bool IsProjectorFeaturePodEnabled() {
+  return IsProjectorEnabled() &&
+         base::FeatureList::IsEnabled(kProjectorFeaturePod);
+}
+
 bool IsQuickAnswersDogfood() {
   return base::FeatureList::IsEnabled(kQuickAnswersDogfood);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 8d3674ab..f34f88a 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -143,6 +143,8 @@
 extern const base::Feature kEnableLocalSearchService;
 extern const base::Feature kEnableOobeChromeVoxHint;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kEnablePciguardUi;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEolWarningNotifications;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kExoOrdinalMotion;
@@ -248,6 +250,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kPrinterStatusDialog;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kProjector;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kProjectorFeaturePod;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kQuickAnswers;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kQuickAnswersDogfood;
@@ -364,6 +367,7 @@
 bool IsClipboardHistoryContextMenuNudgeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeChromeVoxHintEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeScreensPriorityEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPciguardUiEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPhoneHubEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPhoneHubUseBleEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPinAutosubmitFeatureEnabled();
@@ -372,6 +376,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsPinAutosubmitBackfillFeatureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorFeaturePodEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickAnswersDogfood();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickAnswersEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickAnswersRichUiEnabled();
diff --git a/ash/projector/projector_controller.h b/ash/projector/projector_controller.h
index 0d9709f6..6fd48b5 100644
--- a/ash/projector/projector_controller.h
+++ b/ash/projector/projector_controller.h
@@ -56,6 +56,8 @@
   void SetProjectorMetadataControllerForTest(
       std::unique_ptr<ProjectorMetadataController> metadata_controller);
 
+  ProjectorUiController* ui_controller() { return ui_controller_.get(); }
+
  private:
   // Starts the speech recognition session.
   void StartSpeechRecognition();
diff --git a/ash/projector/projector_feature_pod_controller.cc b/ash/projector/projector_feature_pod_controller.cc
new file mode 100644
index 0000000..0d388e10
--- /dev/null
+++ b/ash/projector/projector_feature_pod_controller.cc
@@ -0,0 +1,54 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/projector/projector_feature_pod_controller.h"
+
+#include "ash/projector/projector_controller.h"
+#include "ash/projector/projector_ui_controller.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/system_tray_item_uma_type.h"
+#include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray_controller.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace ash {
+
+ProjectorFeaturePodController::ProjectorFeaturePodController(
+    UnifiedSystemTrayController* tray_controller)
+    : tray_controller_(tray_controller) {
+  // TODO(llin): Observe Projector UI model to update the toggle state.
+}
+
+ProjectorFeaturePodController::~ProjectorFeaturePodController() = default;
+
+FeaturePodButton* ProjectorFeaturePodController::CreateButton() {
+  DCHECK(!button_);
+  button_ = new FeaturePodButton(this, /*is_togglable=*/true);
+  button_->SetVectorIcon(kPaletteTrayIconProjectorIcon);
+  const auto label_text =
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_PROJECTOR_BUTTON_LABEL);
+  button_->SetLabel(label_text);
+  button_->icon_button()->SetTooltipText(label_text);
+  button_->SetLabelTooltip(label_text);
+  button_->SetVisible(
+      !Shell::Get()->session_controller()->IsUserSessionBlocked());
+  // TODO(llin): Update toggle state based on Projector UI model.
+  return button_;
+}
+
+void ProjectorFeaturePodController::OnIconPressed() {
+  // Close the system tray bubble. Deletes |this|.
+  tray_controller_->CloseBubble();
+
+  Shell::Get()->projector_controller()->ui_controller()->ToggleToolbar();
+}
+
+SystemTrayItemUmaType ProjectorFeaturePodController::GetUmaType() const {
+  return SystemTrayItemUmaType::UMA_PROJECTOR;
+}
+
+}  // namespace ash
diff --git a/ash/projector/projector_feature_pod_controller.h b/ash/projector/projector_feature_pod_controller.h
new file mode 100644
index 0000000..31737b56
--- /dev/null
+++ b/ash/projector/projector_feature_pod_controller.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PROJECTOR_PROJECTOR_FEATURE_POD_CONTROLLER_H_
+#define ASH_PROJECTOR_PROJECTOR_FEATURE_POD_CONTROLLER_H_
+
+#include "ash/system/unified/feature_pod_controller_base.h"
+
+namespace ash {
+
+class UnifiedSystemTrayController;
+
+// Controller of a feature pod button that toggles Projector tools.
+class ProjectorFeaturePodController : public FeaturePodControllerBase {
+ public:
+  explicit ProjectorFeaturePodController(
+      UnifiedSystemTrayController* controller);
+  ProjectorFeaturePodController(const ProjectorFeaturePodController&) = delete;
+  ProjectorFeaturePodController& operator=(
+      const ProjectorFeaturePodController&) = delete;
+  ~ProjectorFeaturePodController() override;
+
+  // FeaturePodControllerBase:
+  FeaturePodButton* CreateButton() override;
+  void OnIconPressed() override;
+  SystemTrayItemUmaType GetUmaType() const override;
+
+ private:
+  UnifiedSystemTrayController* const tray_controller_;
+
+  FeaturePodButton* button_ = nullptr;
+};
+
+}  // namespace ash
+
+#endif  // ASH_PROJECTOR_PROJECTOR_FEATURE_POD_CONTROLLER_H_
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc
index def69045..b954a5cc 100644
--- a/ash/projector/projector_ui_controller.cc
+++ b/ash/projector/projector_ui_controller.cc
@@ -32,6 +32,8 @@
 ProjectorUiController::~ProjectorUiController() = default;
 
 void ProjectorUiController::ShowToolbar() {}
+void ProjectorUiController::CloseToolbar() {}
+void ProjectorUiController::ToggleToolbar() {}
 
 void ProjectorUiController::OnKeyIdeaMarked() {
   ShowToast(kMarkedKeyIdeaToastId, IDS_ASH_PROJECTOR_KEY_IDEA_MARKED,
diff --git a/ash/projector/projector_ui_controller.h b/ash/projector/projector_ui_controller.h
index 7f798ef..e0b67cdc 100644
--- a/ash/projector/projector_ui_controller.h
+++ b/ash/projector/projector_ui_controller.h
@@ -20,8 +20,13 @@
   ProjectorUiController& operator=(const ProjectorUiController&) = delete;
   virtual ~ProjectorUiController();
 
-  // Virtual for testing.
+  // Show Projector toolbar. Virtual for testing.
   virtual void ShowToolbar();
+  // Close Projector toolbar. Virtual for testing.
+  virtual void CloseToolbar();
+  // Toggle Projector toolbar based on the toolbar visibility state.
+  // Virtual for testing.
+  virtual void ToggleToolbar();
   // Invoked when key idea is marked to show a toast. Virtual for testing.
   virtual void OnKeyIdeaMarked();
   // Invoked when transcription is available for rendering. Virtual for testing.
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index ee40b1e4..ae26d37 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -67,7 +67,7 @@
     "InteractiveWindowCycleList", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kManagedDeviceUIRedesign{"ManagedDeviceUIRedesign",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kMediaSessionNotification{"MediaSessionNotification",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
@@ -125,9 +125,6 @@
 const base::Feature kStylusBatteryStatus{"StylusBatteryStatus",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kSystemTrayMicGainSetting{"SystemTrayMicGainSetting",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kWebUITabStripTabDragIntegration{
     "WebUITabStripTabDragIntegration", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -286,10 +283,6 @@
   return base::FeatureList::IsEnabled(kStylusBatteryStatus);
 }
 
-bool IsSystemTrayMicGainSettingEnabled() {
-  return base::FeatureList::IsEnabled(kSystemTrayMicGainSetting);
-}
-
 bool IsDisplayIdentificationEnabled() {
   return base::FeatureList::IsEnabled(kDisplayIdentification);
 }
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 2c477c3..93118c6f 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -169,10 +169,6 @@
 // Enables battery indicator for styluses in the palette tray
 ASH_PUBLIC_EXPORT extern const base::Feature kStylusBatteryStatus;
 
-// Enables sliders for setting mic gain levels in the more audio settings
-// section in the system tray.
-ASH_PUBLIC_EXPORT extern const base::Feature kSystemTrayMicGainSetting;
-
 // Enables special handling of Chrome tab drags from a WebUI tab strip.
 // These will be treated similarly to a window drag, showing split view
 // indicators in tablet mode, etc. The functionality is behind a flag
@@ -257,8 +253,6 @@
 
 ASH_PUBLIC_EXPORT bool AreContextualNudgesEnabled();
 
-ASH_PUBLIC_EXPORT bool IsSystemTrayMicGainSettingEnabled();
-
 ASH_PUBLIC_EXPORT bool IsStylusBatteryStatusEnabled();
 
 ASH_PUBLIC_EXPORT bool IsDisplayIdentificationEnabled();
diff --git a/ash/public/cpp/login_accelerators.cc b/ash/public/cpp/login_accelerators.cc
index c65442b..a530ed79 100644
--- a/ash/public/cpp/login_accelerators.cc
+++ b/ash/public/cpp/login_accelerators.cc
@@ -58,6 +58,10 @@
        kEnableConsumerKiosk,
        ui::VKEY_K, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
        false, kScopeOobe,
+    }, {
+       kLaunchDiagnostics,
+       ui::VKEY_ESCAPE, ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
+       true, kScopeOobe | kScopeLogin,
     },
 };
 // clang-format on
diff --git a/ash/public/cpp/login_accelerators.h b/ash/public/cpp/login_accelerators.h
index 350bd04..8ca1a37a 100644
--- a/ash/public/cpp/login_accelerators.h
+++ b/ash/public/cpp/login_accelerators.h
@@ -38,6 +38,7 @@
   kEditDeviceRequisition,
   kDeviceRequisitionRemora,
   kStartDemoMode,
+  kLaunchDiagnostics,
 };
 
 struct LoginAcceleratorData {
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index f8834c4..a4296e37 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -57,6 +57,7 @@
     "dictation_off_newui.icon",
     "dictation_on.icon",
     "dictation_on_newui.icon",
+    "do_not_disturb_disabled.icon",
     "dogfood.icon",
     "eight_files.icon",
     "files_app.icon",
@@ -153,6 +154,7 @@
     "palette_tray_icon_laser_pointer_light_mode.icon",
     "palette_tray_icon_magnify.icon",
     "palette_tray_icon_metalayer.icon",
+    "palette_tray_icon_projector.icon",
     "phone_hub_battery_saver.icon",
     "phone_hub_battery_saver_outline.icon",
     "phone_hub_default_favicon.icon",
diff --git a/ash/resources/vector_icons/do_not_disturb_disabled.icon b/ash/resources/vector_icons/do_not_disturb_disabled.icon
new file mode 100644
index 0000000..7df1f39
--- /dev/null
+++ b/ash/resources/vector_icons/do_not_disturb_disabled.icon
@@ -0,0 +1,40 @@
+// Copyright 2021 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.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 1.87f, 2.05f,
+LINE_TO, 0.46f, 3.46f,
+LINE_TO, 3.04f, 6.04f,
+CUBIC_TO, 2.38f, 7.21f, 2, 8.56f, 2, 10,
+CUBIC_TO, 2, 14.42f, 5.58f, 18, 10, 18,
+CUBIC_TO, 11.44f, 18, 12.79f, 17.62f, 13.96f, 16.96f,
+LINE_TO, 16.02f, 19.02f,
+LINE_TO, 17.43f, 17.6f,
+LINE_TO, 1.87f, 2.05f,
+CLOSE,
+MOVE_TO, 6, 9,
+LINE_TO, 4.53f, 7.53f,
+CUBIC_TO, 4.19f, 8.28f, 4, 9.12f, 4, 10,
+CUBIC_TO, 4, 13.31f, 6.69f, 16, 10, 16,
+CUBIC_TO, 10.88f, 16, 11.72f, 15.81f, 12.47f, 15.47f,
+LINE_TO, 8, 11,
+H_LINE_TO, 6,
+LINE_TO, 6, 9,
+CLOSE,
+MOVE_TO, 16.55f, 14.6f,
+CUBIC_TO, 17.46f, 13.3f, 18, 11.71f, 18, 10,
+CUBIC_TO, 18, 5.58f, 14.42f, 2, 10, 2,
+CUBIC_TO, 8.29f, 2, 6.7f, 2.54f, 5.4f, 3.45f,
+LINE_TO, 6.85f, 4.9f,
+CUBIC_TO, 7.76f, 4.33f, 8.84f, 4, 10, 4,
+CUBIC_TO, 13.31f, 4, 16, 6.69f, 16, 10,
+CUBIC_TO, 16, 11.16f, 15.67f, 12.24f, 15.1f, 13.15f,
+LINE_TO, 16.55f, 14.6f,
+CLOSE,
+MOVE_TO, 12.95f, 11,
+H_LINE_TO, 14,
+V_LINE_TO, 9,
+H_LINE_TO, 10.95f,
+LINE_TO, 12.95f, 11,
+CLOSE
\ No newline at end of file
diff --git a/ash/resources/vector_icons/palette_tray_icon_projector.icon b/ash/resources/vector_icons/palette_tray_icon_projector.icon
new file mode 100644
index 0000000..9cb36b0
--- /dev/null
+++ b/ash/resources/vector_icons/palette_tray_icon_projector.icon
@@ -0,0 +1,37 @@
+// Copyright 2021 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 4.59f, 6.89f,
+R_CUBIC_TO, 0.7f, -0.71f, 1.4f, -1.35f, 1.71f, -1.22f,
+R_CUBIC_TO, 0.5f, 0.2f, 0, 1.03f, -0.3f, 1.52f,
+R_CUBIC_TO, -0.25f, 0.42f, -2.86f, 3.89f, -2.86f, 6.31f,
+R_CUBIC_TO, 0, 1.28f, 0.48f, 2.34f, 1.34f, 2.98f,
+R_CUBIC_TO, 0.75f, 0.56f, 1.74f, 0.73f, 2.64f, 0.46f,
+R_CUBIC_TO, 1.07f, -0.31f, 1.95f, -1.4f, 3.06f, -2.77f,
+R_CUBIC_TO, 1.21f, -1.49f, 2.83f, -3.44f, 4.08f, -3.44f,
+R_CUBIC_TO, 1.63f, 0, 1.65f, 1.01f, 1.76f, 1.79f,
+R_CUBIC_TO, -3.78f, 0.64f, -5.38f, 3.67f, -5.38f, 5.37f,
+R_CUBIC_TO, 0, 1.7f, 1.44f, 3.09f, 3.21f, 3.09f,
+R_CUBIC_TO, 1.63f, 0, 4.29f, -1.33f, 4.69f, -6.1f,
+H_LINE_TO, 21,
+R_V_LINE_TO, -2.5f,
+R_H_LINE_TO, -2.47f,
+R_CUBIC_TO, -0.15f, -1.65f, -1.09f, -4.2f, -4.03f, -4.2f,
+R_CUBIC_TO, -2.25f, 0, -4.18f, 1.91f, -4.94f, 2.84f,
+R_CUBIC_TO, -0.58f, 0.73f, -2.06f, 2.48f, -2.29f, 2.72f,
+R_CUBIC_TO, -0.25f, 0.3f, -0.68f, 0.84f, -1.11f, 0.84f,
+R_CUBIC_TO, -0.45f, 0, -0.72f, -0.83f, -0.36f, -1.92f,
+R_CUBIC_TO, 0.35f, -1.09f, 1.4f, -2.86f, 1.85f, -3.52f,
+R_CUBIC_TO, 0.78f, -1.14f, 1.3f, -1.92f, 1.3f, -3.28f,
+CUBIC_TO, 8.95f, 3.69f, 7.31f, 3, 6.44f, 3,
+CUBIC_TO, 5.12f, 3, 3.97f, 4, 3.72f, 4.25f,
+R_CUBIC_TO, -0.36f, 0.36f, -0.66f, 0.66f, -0.88f, 0.93f,
+R_LINE_TO, 1.75f, 1.71f,
+CLOSE,
+R_MOVE_TO, 9.29f, 11.66f,
+R_CUBIC_TO, -0.31f, 0, -0.74f, -0.26f, -0.74f, -0.72f,
+R_CUBIC_TO, 0, -0.6f, 0.73f, -2.2f, 2.87f, -2.76f,
+R_CUBIC_TO, -0.3f, 2.69f, -1.43f, 3.48f, -2.13f, 3.48f,
+CLOSE
\ No newline at end of file
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 05eabed..1603942 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -106,6 +106,14 @@
   return cros_colors::ResolveColor(TypeToColorName(type), is_dark_mode);
 }
 
+// Notify all the other components to update on the color mode changes. Only
+// Chrome browser is notified currently, will include WebUI, Arc etc later.
+void NotifyColorModeChanges(bool is_dark_mode_enabled) {
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+  native_theme->set_use_dark_colors(is_dark_mode_enabled);
+  native_theme->NotifyObservers();
+}
+
 }  // namespace
 
 AshColorProvider::AshColorProvider() {
@@ -161,6 +169,13 @@
   NotifyColorModeThemedPrefChange();
 }
 
+void AshColorProvider::OnSessionStateChanged(
+    session_manager::SessionState state) {
+  if (!features::IsDarkLightModeEnabled())
+    return;
+  NotifyColorModeChanges(IsDarkModeEnabled());
+}
+
 SkColor AshColorProvider::GetShieldLayerColor(ShieldLayerType type) const {
   constexpr int kAlphas[] = {kAlpha20, kAlpha40, kAlpha60, kAlpha80, kAlpha90};
   DCHECK_LT(static_cast<size_t>(type), base::size(kAlphas));
@@ -375,6 +390,7 @@
   active_user_pref_service_->SetBoolean(prefs::kDarkModeEnabled,
                                         !IsDarkModeEnabled());
   active_user_pref_service_->CommitPendingWrite();
+  NotifyColorModeChanges(IsDarkModeEnabled());
 }
 
 void AshColorProvider::UpdateColorModeThemed(bool is_themed) {
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index c6eb437f..820ae56b 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -57,6 +57,7 @@
 
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* prefs) override;
+  void OnSessionStateChanged(session_manager::SessionState state) override;
 
   // ColorProvider:
   SkColor GetShieldLayerColor(ShieldLayerType type) const override;
diff --git a/ash/system/audio/audio_detailed_view.cc b/ash/system/audio/audio_detailed_view.cc
index 534ab1a..a25ddb2 100644
--- a/ash/system/audio/audio_detailed_view.cc
+++ b/ash/system/audio/audio_detailed_view.cc
@@ -163,10 +163,8 @@
         AddScrollListCheckableItem(GetAudioDeviceName(device), device.active);
     device_map_[container] = device;
 
-    if (features::IsSystemTrayMicGainSettingEnabled()) {
-      AddScrollListChild(mic_gain_controller_->CreateMicGainSlider(
-          device.id, device.IsInternalMic()));
-    }
+    AddScrollListChild(mic_gain_controller_->CreateMicGainSlider(
+        device.id, device.IsInternalMic()));
   }
 
   scroll_content()->SizeToPreferredSize();
diff --git a/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc b/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
index 6df39400..e7d1273 100644
--- a/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
+++ b/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
@@ -116,9 +116,6 @@
   void SetUp() override {
     AshTestBase::SetUp();
 
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kSystemTrayMicGainSetting);
-
     fake_manager_ = std::make_unique<FakeMediaControllerManager>();
     tray_model_ = std::make_unique<UnifiedSystemTrayModel>(nullptr);
     tray_controller_ =
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index 71f479ce..4440f8e 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -185,14 +185,10 @@
     SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kHorizontal,
         gfx::Insets((kTrayItemSize -
-                     GetDefaultSizeOfVectorIcon(vector_icons::kHeadsetIcon)) /
+                     GetDefaultSizeOfVectorIcon(kUnifiedMenuExpandIcon)) /
                     2),
         2));
 
-    if (!features::IsSystemTrayMicGainSettingEnabled()) {
-      headset_image_ = AddChildView(std::make_unique<views::ImageView>());
-      headset_image_->SetCanProcessEventsWithinSubtree(false);
-    }
     more_image_ = AddChildView(std::make_unique<views::ImageView>());
     more_image_->SetCanProcessEventsWithinSubtree(false);
     SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO));
@@ -205,10 +201,7 @@
   void OnThemeChanged() override {
     UnifiedVolumeViewButton::OnThemeChanged();
     const SkColor icon_color = GetIconColor();
-    if (headset_image_) {
-      headset_image_->SetImage(
-          CreateVectorIcon(vector_icons::kHeadsetIcon, icon_color));
-    }
+
     DCHECK(more_image_);
     auto icon_rotation = base::i18n::IsRTL()
                              ? SkBitmapOperations::ROTATION_270_CW
@@ -218,7 +211,6 @@
   }
 
  private:
-  views::ImageView* headset_image_ = nullptr;
   views::ImageView* more_image_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(MoreButton);
@@ -285,10 +277,6 @@
       Shell::Get()->session_controller()->GetActivePrefService()->GetBoolean(
           prefs::kLiveCaptionEnabled));
 
-  more_button_->SetVisible(CrasAudioHandler::Get()->has_alternative_input() ||
-                           CrasAudioHandler::Get()->has_alternative_output() ||
-                           features::IsSystemTrayMicGainSettingEnabled());
-
   // Slider's value is in finer granularity than audio volume level(0.01),
   // there will be a small discrepancy between slider's value and volume level
   // on audio side. To avoid the jittering in slider UI, use the slider's
diff --git a/ash/system/holding_space/holding_space_item_chip_view.cc b/ash/system/holding_space/holding_space_item_chip_view.cc
index a4a23899..e92157f 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.cc
+++ b/ash/system/holding_space/holding_space_item_chip_view.cc
@@ -18,6 +18,7 @@
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
@@ -25,6 +26,7 @@
 #include "ui/views/painter.h"
 
 namespace ash {
+namespace {
 
 // Appearance.
 constexpr int kChildSpacing = 8;
@@ -65,56 +67,29 @@
   const gfx::InsetsF insets_;
 };
 
-// LabelMaskLayerOwner ---------------------------------------------------------
+// PaintCallbackLabel ----------------------------------------------------------
 
-class LabelMaskLayerOwner : public ui::LayerOwner, public ui::LayerDelegate {
+class PaintCallbackLabel : public views::Label {
  public:
-  LabelMaskLayerOwner()
-      : ui::LayerOwner(std::make_unique<ui ::Layer>(ui::LAYER_TEXTURED)) {
-    layer()->set_delegate(this);
-    layer()->SetFillsBoundsOpaquely(false);
-  }
+  using PaintCallback = base::RepeatingCallback<void(gfx::Canvas* canvas)>;
 
-  LabelMaskLayerOwner(const LabelMaskLayerOwner&) = delete;
-  LabelMaskLayerOwner& operator=(const LabelMaskLayerOwner&) = delete;
-  ~LabelMaskLayerOwner() override = default;
+  explicit PaintCallbackLabel(PaintCallback callback) : callback_(callback) {}
+  PaintCallbackLabel(const PaintCallbackLabel&) = delete;
+  PaintCallbackLabel& operator=(const PaintCallbackLabel&) = delete;
+  ~PaintCallbackLabel() override = default;
 
  private:
-  // ui::LayerDelegate:
-  void OnPaintLayer(const ui::PaintContext& context) override {
-    const gfx::Size size = layer()->size();
-
-    views::PaintInfo paint_info =
-        views::PaintInfo::CreateRootPaintInfo(context, size);
-    const auto& paint_recording_size = paint_info.paint_recording_size();
-
-    // Pass the scale factor when constructing `PaintRecorder` so the mask layer
-    // size is not incorrectly rounded (see https://crbug.com/921274).
-    ui::PaintRecorder recorder(
-        context, paint_info.paint_recording_size(),
-        static_cast<float>(paint_recording_size.width()) / size.width(),
-        static_cast<float>(paint_recording_size.height()) / size.height(),
-        /*cache*/ nullptr);
-
-    // Flip canvas for RTL.
-    gfx::ScopedCanvas canvas(recorder.canvas());
-    canvas.FlipIfRTL(size.width());
-
-    cc::PaintFlags flags;
-    flags.setAntiAlias(false);
-
-    gfx::Point gradient_end(size.width() - kHoldingSpaceIconSize, 0);
-    gfx::Point gradient_start(gradient_end.x() - kLabelMaskGradientWidth, 0);
-    flags.setShader(gfx::CreateGradientShader(
-        gradient_start, gradient_end, SK_ColorBLACK, SK_ColorTRANSPARENT));
-
-    recorder.canvas()->DrawRect(gfx::Rect(size), flags);
+  // views::Label:
+  void OnPaint(gfx::Canvas* canvas) override {
+    views::Label::OnPaint(canvas);
+    callback_.Run(canvas);
   }
 
-  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
-                                  float new_device_scale_factor) override {}
+  PaintCallback callback_;
 };
 
+}  // namespace
+
 // HoldingSpaceItemChipView ----------------------------------------------------
 
 HoldingSpaceItemChipView::HoldingSpaceItemChipView(
@@ -128,6 +103,7 @@
 
   SetPreferredSize(gfx::Size(kPreferredWidth, kPreferredHeight));
 
+  // Image.
   image_ = AddChildView(std::make_unique<RoundedImageView>(
       kHoldingSpaceChipIconSize / 2, RoundedImageView::Alignment::kLeading));
 
@@ -144,28 +120,29 @@
 
   UpdateImage();
 
-  label_and_pin_button_container_ =
+  auto* label_and_pin_button_container =
       AddChildView(std::make_unique<views::View>());
-  layout->SetFlexForView(label_and_pin_button_container_, 1);
-
-  label_and_pin_button_container_->SetLayoutManager(
+  label_and_pin_button_container->SetLayoutManager(
       std::make_unique<views::FillLayout>());
+  layout->SetFlexForView(label_and_pin_button_container, 1);
 
-  label_ = label_and_pin_button_container_->AddChildView(
-      holding_space_util::CreateLabel(holding_space_util::LabelStyle::kChip));
+  // Label.
+  label_ = label_and_pin_button_container->AddChildView(
+      std::make_unique<PaintCallbackLabel>(
+          base::BindRepeating(&HoldingSpaceItemChipView::OnPaintLabelMask,
+                              base::Unretained(this))));
   label_->SetBorder(views::CreateEmptyBorder(kLabelMargins));
   label_->SetElideBehavior(gfx::ELIDE_MIDDLE);
   label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
   label_->SetText(item->text());
-
-  label_mask_layer_owner_ = std::make_unique<LabelMaskLayerOwner>();
-
   label_->SetPaintToLayer();
   label_->layer()->SetFillsBoundsOpaquely(false);
-  label_->layer()->SetMaskLayer(label_mask_layer_owner_->layer());
 
+  holding_space_util::ApplyStyle(label_, holding_space_util::LabelStyle::kChip);
+
+  // Pin.
   views::View* pin_button_container =
-      label_and_pin_button_container_->AddChildView(
+      label_and_pin_button_container->AddChildView(
           std::make_unique<views::View>());
 
   auto* pin_layout =
@@ -176,7 +153,7 @@
   pin_layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  AddPin(/*parent=*/pin_button_container);
+  pin_ = AddPin(/*parent=*/pin_button_container);
 }
 
 HoldingSpaceItemChipView::~HoldingSpaceItemChipView() = default;
@@ -196,14 +173,32 @@
 }
 
 void HoldingSpaceItemChipView::OnPinVisiblityChanged(bool pin_visible) {
-  if (label_mask_layer_owner_->layer()->bounds() !=
-      label_and_pin_button_container_->bounds()) {
-    // Mask layer has the same size as the label container so that the gradient
-    // ends at the end of the container.
-    label_mask_layer_owner_->layer()->SetBounds(
-        label_and_pin_button_container_->bounds());
+  // The `label_` must be repainted to update its mask for `pin_` visibility.
+  label_->SchedulePaint();
+}
+
+void HoldingSpaceItemChipView::OnPaintLabelMask(gfx::Canvas* canvas) {
+  // When the `pin_` is not visible no masking is necessary.
+  if (!pin_->GetVisible())
+    return;
+
+  // When the `pin_` is visible, `label_` fades out its tail to avoid overlap.
+  gfx::Point gradient_start, gradient_end;
+  if (base::i18n::IsRTL()) {
+    gradient_end.set_x(pin_->width());
+    gradient_start.set_x(gradient_end.x() + kLabelMaskGradientWidth);
+  } else {
+    gradient_end.set_x(label_->width() - pin_->width());
+    gradient_start.set_x(gradient_end.x() - kLabelMaskGradientWidth);
   }
-  label_mask_layer_owner_->layer()->SetVisible(pin_visible);
+
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setBlendMode(SkBlendMode::kDstIn);
+  flags.setShader(gfx::CreateGradientShader(
+      gradient_start, gradient_end, SK_ColorBLACK, SK_ColorTRANSPARENT));
+
+  canvas->DrawRect(label_->GetLocalBounds(), flags);
 }
 
 void HoldingSpaceItemChipView::UpdateImage() {
diff --git a/ash/system/holding_space/holding_space_item_chip_view.h b/ash/system/holding_space/holding_space_item_chip_view.h
index 2904271..f8e7a9e 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.h
+++ b/ash/system/holding_space/holding_space_item_chip_view.h
@@ -12,10 +12,6 @@
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "ui/views/metadata/metadata_header_macros.h"
 
-namespace ui {
-class LayerOwner;
-}  // namespace ui
-
 namespace views {
 class Label;
 }  // namespace views
@@ -44,13 +40,16 @@
   void OnHoldingSpaceItemUpdated(const HoldingSpaceItem* item) override;
   void OnPinVisiblityChanged(bool pin_visible) override;
 
+  // Invoked during `label_`'s paint sequence to paint its optional mask. Note
+  // that `label_` is only masked when `pin_` is visible to avoid overlapping.
+  void OnPaintLabelMask(gfx::Canvas* canvas);
+
   void UpdateImage();
 
-  std::unique_ptr<ui::LayerOwner> label_mask_layer_owner_;
-
+  // Owned by view hierarchy.
   RoundedImageView* image_ = nullptr;
   views::Label* label_ = nullptr;
-  views::View* label_and_pin_button_container_ = nullptr;
+  views::View* pin_ = nullptr;
 
   base::CallbackListSubscription image_subscription_;
 };
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 6fe12a78..fc4c854 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -112,7 +112,7 @@
   return icon;
 }
 
-// TODO(crbug.com/1171059): Refine UI upon delivery of spec.
+// TODO(crbug.com/1171059): Add ripple effects.
 // Creates the overlay to be drawn over all other child views to indicate that
 // the parent view is a drop target and is capable of handling the current drag
 // payload.
@@ -122,16 +122,18 @@
   drop_target_overlay->SetLayoutManager(std::make_unique<views::FillLayout>());
 
   // Layer.
-  drop_target_overlay->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  drop_target_overlay->layer()->SetColor(gfx::kGoogleGrey400);
+  drop_target_overlay->SetPaintToLayer();
+  drop_target_overlay->layer()->SetFillsBoundsOpaquely(false);
 
   // Icon.
   auto* icon =
       drop_target_overlay->AddChildView(std::make_unique<views::ImageView>());
   icon->SetHorizontalAlignment(views::ImageView::Alignment::kCenter);
   icon->SetVerticalAlignment(views::ImageView::Alignment::kCenter);
-  icon->SetImage(gfx::CreateVectorIcon(views::kUnpinIcon, kHoldingSpaceIconSize,
-                                       gfx::kGoogleGrey700));
+  icon->SetImage(gfx::CreateVectorIcon(
+      views::kUnpinIcon, kHoldingSpaceIconSize,
+      AshColorProvider::Get()->GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kIconColorPrimary)));
   icon->SetPaintToLayer();
   icon->layer()->SetFillsBoundsOpaquely(false);
 
diff --git a/ash/system/holding_space/holding_space_util.cc b/ash/system/holding_space/holding_space_util.cc
index f2eec46..c3b7ac0 100644
--- a/ash/system/holding_space/holding_space_util.cc
+++ b/ash/system/holding_space/holding_space_util.cc
@@ -75,9 +75,7 @@
             observer);
 }
 
-std::unique_ptr<views::Label> CreateLabel(LabelStyle style,
-                                          const base::string16& text) {
-  auto label = std::make_unique<views::Label>(text);
+void ApplyStyle(views::Label* label, LabelStyle style) {
   label->SetAutoColorReadabilityEnabled(false);
   label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kTextColorPrimary));
@@ -100,7 +98,12 @@
                                        gfx::Font::Weight::MEDIUM));
       break;
   }
+}
 
+std::unique_ptr<views::Label> CreateLabel(LabelStyle style,
+                                          const base::string16& text) {
+  auto label = std::make_unique<views::Label>(text);
+  ApplyStyle(label.get(), style);
   return label;
 }
 
diff --git a/ash/system/holding_space/holding_space_util.h b/ash/system/holding_space/holding_space_util.h
index f8e1fa8..a1e0b9c 100644
--- a/ash/system/holding_space/holding_space_util.h
+++ b/ash/system/holding_space/holding_space_util.h
@@ -41,6 +41,9 @@
   kHeader,
 };
 
+// Applies the specified `style` to the given `label`.
+void ApplyStyle(views::Label* label, LabelStyle style);
+
 // Creates a label with optional `text` matching the specified `style`.
 std::unique_ptr<views::Label> CreateLabel(
     LabelStyle style,
diff --git a/ash/system/message_center/message_center_utils.cc b/ash/system/message_center/message_center_utils.cc
index 8a5af30..8af7c407 100644
--- a/ash/system/message_center/message_center_utils.cc
+++ b/ash/system/message_center/message_center_utils.cc
@@ -4,9 +4,6 @@
 
 #include "ash/system/message_center/message_center_utils.h"
 
-#include "ash/media/media_notification_constants.h"
-#include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ui/message_center/message_center.h"
 
 namespace ash {
@@ -33,30 +30,6 @@
   return sorted_notifications;
 }
 
-size_t GetNotificationCount() {
-  // If flag is set, do not include media notifications in count.
-  // TODO(crbug.com/1111881) This code can be removed when OS media controls are
-  // launched (expected by M90).
-  const bool skip_media_notification =
-      base::FeatureList::IsEnabled(features::kMediaNotificationsCounter);
-
-  size_t count = 0;
-  for (message_center::Notification* notification :
-       message_center::MessageCenter::Get()->GetVisibleNotifications()) {
-    const std::string& notifier = notification->notifier_id().id;
-    // Don't count these notifications since we have `CameraMicTrayItemView` to
-    // show indicators on the systray.
-    if (notifier == kVmCameraMicNotifierId)
-      continue;
-
-    if (skip_media_notification && notifier == kMediaSessionNotifierId)
-      continue;
-
-    ++count;
-  }
-  return count;
-}
-
 }  // namespace message_center_utils
 
 }  // namespace ash
diff --git a/ash/system/message_center/message_center_utils.h b/ash/system/message_center/message_center_utils.h
index ca9758a..63fb97a69 100644
--- a/ash/system/message_center/message_center_utils.h
+++ b/ash/system/message_center/message_center_utils.h
@@ -5,7 +5,6 @@
 #ifndef ASH_SYSTEM_MESSAGE_CENTER_MESSAGE_CENTER_UTILS_H_
 #define ASH_SYSTEM_MESSAGE_CENTER_MESSAGE_CENTER_UTILS_H_
 
-#include "ash/ash_export.h"
 #include "ui/message_center/public/cpp/notification.h"
 
 namespace ash {
@@ -24,11 +23,6 @@
 // CompareNotifications() above for the sorting order.
 std::vector<message_center::Notification*> GetSortedVisibleNotifications();
 
-// Returns total notifications count, with a filter to not count some of them
-// (These notifications such as camera, media controls, etc. don't need an
-// indicator in status area since they already have a dedicated tray item).
-size_t ASH_EXPORT GetNotificationCount();
-
 }  // namespace message_center_utils
 
 }  // namespace ash
diff --git a/ash/system/message_center/message_center_utils_unittest.cc b/ash/system/message_center/message_center_utils_unittest.cc
deleted file mode 100644
index 633411a9..0000000
--- a/ash/system/message_center/message_center_utils_unittest.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/system/message_center/message_center_utils.h"
-
-#include "ash/media/media_notification_constants.h"
-#include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/vm_camera_mic_constants.h"
-#include "ash/test/ash_test_base.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "ui/message_center/message_center.h"
-
-namespace ash {
-
-namespace message_center_utils {
-
-namespace {
-
-void AddNotification(const std::string& notification_id,
-                     const std::string& app_id) {
-  message_center::MessageCenter::Get()->AddNotification(
-      std::make_unique<message_center::Notification>(
-          message_center::NOTIFICATION_TYPE_BASE_FORMAT, notification_id,
-          base::UTF8ToUTF16("test_title"), base::UTF8ToUTF16("test message"),
-          gfx::Image(), /*display_source=*/base::string16(), GURL(),
-          message_center::NotifierId(message_center::NotifierType::APPLICATION,
-                                     app_id),
-          message_center::RichNotificationData(),
-          new message_center::NotificationDelegate()));
-}
-
-}  // namespace
-
-class MessageCenterUtilsTest : public AshTestBase,
-                               public testing::WithParamInterface<bool> {
- public:
-  MessageCenterUtilsTest() = default;
-  MessageCenterUtilsTest(const MessageCenterUtilsTest&) = delete;
-  MessageCenterUtilsTest& operator=(const MessageCenterUtilsTest&) = delete;
-  ~MessageCenterUtilsTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-    scoped_feature_list_.InitWithFeatureState(
-        features::kMediaNotificationsCounter,
-        IsMediaNotificationsCounterEnabled());
-  }
-
-  bool IsMediaNotificationsCounterEnabled() { return GetParam(); }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    MessageCenterUtilsTest,
-    testing::Bool() /* IsMediaNotificationsCounterEnabled() */);
-
-TEST_P(MessageCenterUtilsTest, TotalNotificationCount) {
-  EXPECT_EQ(0u, GetNotificationCount());
-
-  // VM camera/mic notifications are ignored by the counter.
-  AddNotification("0", kVmCameraMicNotifierId);
-  EXPECT_EQ(0u, GetNotificationCount());
-
-  AddNotification("1", kMediaSessionNotifierId);
-  // Counter should ignore media notifications when feature is enabled.
-  size_t expected_count = IsMediaNotificationsCounterEnabled() ? 0u : 1u;
-  EXPECT_EQ(expected_count, GetNotificationCount());
-}
-
-}  // namespace message_center_utils
-
-}  // namespace ash
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index ca926c5..b0f96e35 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -552,10 +552,10 @@
       ContentLayerType::kIconColorPrimary);
   if (is_quiet_mode) {
     quiet_mode_icon_->SetImage(gfx::CreateVectorIcon(
-        kNotificationCenterDoNotDisturbOnIcon, kMenuIconSize, icon_color));
+        kSystemTrayDoNotDisturbIcon, kMenuIconSize, icon_color));
   } else {
     quiet_mode_icon_->SetImage(gfx::CreateVectorIcon(
-        kNotificationCenterDoNotDisturbOffIcon, kMenuIconSize, icon_color));
+        kDoNotDisturbDisabledIcon, kMenuIconSize, icon_color));
   }
 }
 
diff --git a/ash/system/tray/system_tray_item_uma_type.h b/ash/system/tray/system_tray_item_uma_type.h
index 5529749f..e09e425 100644
--- a/ash/system/tray/system_tray_item_uma_type.h
+++ b/ash/system/tray/system_tray_item_uma_type.h
@@ -46,7 +46,8 @@
   UMA_PRIVACY_SCREEN = 29,
   UMA_DARK_MODE = 30,
   UMA_NEARBY_SHARE = 31,
-  UMA_COUNT = 32,
+  UMA_PROJECTOR = 32,
+  UMA_COUNT = 33,
 };
 
 }  // namespace ash
diff --git a/ash/system/unified/notification_counter_view.cc b/ash/system/unified/notification_counter_view.cc
index 0bc4bd41..eadd00d 100644
--- a/ash/system/unified/notification_counter_view.cc
+++ b/ash/system/unified/notification_counter_view.cc
@@ -14,11 +14,8 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
-#include "ash/system/message_center/message_center_utils.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_utils.h"
-#include "ash/system/unified/notification_icons_controller.h"
-#include "ash/system/unified/unified_system_tray.h"
 #include "base/i18n/number_formatting.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
@@ -92,16 +89,11 @@
 
 }  // namespace
 
-NotificationCounterView::NotificationCounterView(
-    UnifiedSystemTray* tray,
-    NotificationIconsController* controller)
-    : TrayItemView(tray->shelf()), controller_(controller) {
-  system_tray_model_observation_.Observe(tray->model());
+NotificationCounterView::NotificationCounterView(Shelf* shelf)
+    : TrayItemView(shelf) {
   CreateImageView();
   SetVisible(false);
   Shell::Get()->session_controller()->AddObserver(this);
-
-  OnSystemTrayButtonSizeChanged(tray->model()->GetSystemTrayButtonSize());
 }
 
 NotificationCounterView::~NotificationCounterView() {
@@ -112,14 +104,33 @@
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
 
-  size_t notification_count = message_center_utils::GetNotificationCount();
+  // If flag is set, do not include media notifications in count.
+  // TODO(crbug.com/1111881) This code can be removed when OS media controls are
+  // launched (expected by M90).
+  const bool dont_count_media_notification =
+      base::FeatureList::IsEnabled(features::kMediaNotificationsCounter);
 
-  // If we are currently showing icons of some notifications in the tray, this
-  // counter should not be shown.
-  const bool tray_notification_icons_shown =
-      icons_view_visible_ && controller_ &&
-      controller_->TrayItemHasNotification();
-  if (notification_count == 0 || tray_notification_icons_shown ||
+  const message_center::NotificationList::Notifications& visible =
+      message_center::MessageCenter::Get()->GetVisibleNotifications();
+
+  size_t notification_count = std::count_if(
+      visible.begin(), visible.end(),
+      [dont_count_media_notification](
+          message_center::Notification* notification) {
+        const std::string& notifier = notification->notifier_id().id;
+        // Don't count these notifications since we have `CameraMicTrayItemView`
+        // to show indicators on the systray.
+        if (notifier == kVmCameraMicNotifierId) {
+          return false;
+        }
+        if (dont_count_media_notification &&
+            notifier == kMediaSessionNotifierId) {
+          return false;
+        }
+        return true;
+      });
+
+  if (notification_count == 0 ||
       message_center::MessageCenter::Get()->IsQuietMode() ||
       !session_controller->ShouldShowNotificationTray() ||
       (session_controller->IsScreenLocked() &&
@@ -153,37 +164,10 @@
   Update();
 }
 
-void NotificationCounterView::OnSystemTrayButtonSizeChanged(
-    UnifiedSystemTrayModel::SystemTrayButtonSize system_tray_size) {
-  icons_view_visible_ =
-      system_tray_size != UnifiedSystemTrayModel::SystemTrayButtonSize::kSmall;
-  Update();
-}
-
 const char* NotificationCounterView::GetClassName() const {
   return "NotificationCounterView";
 }
 
-HiddenNotificationCountView::HiddenNotificationCountView(Shelf* shelf)
-    : TrayItemView(shelf) {
-  CreateLabel();
-  SetupLabelForTray(label());
-  SetBorder(views::CreateEmptyBorder(kUnifiedTrayTextTopPadding, 0, 0,
-                                     kUnifiedTrayTextRightPadding));
-  label()->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
-      AshColorProvider::ContentLayerType::kIconColorPrimary));
-}
-
-HiddenNotificationCountView::~HiddenNotificationCountView() = default;
-
-void HiddenNotificationCountView::HandleLocaleChange() {
-  // TODO(crbug.com/1161557): Finish this function.
-}
-
-const char* HiddenNotificationCountView::GetClassName() const {
-  return "HiddenNotificationCountView";
-}
-
 QuietModeView::QuietModeView(Shelf* shelf) : TrayItemView(shelf) {
   CreateImageView();
   image_view()->SetTooltipText(
diff --git a/ash/system/unified/notification_counter_view.h b/ash/system/unified/notification_counter_view.h
index fcc8de3..5fbf695 100644
--- a/ash/system/unified/notification_counter_view.h
+++ b/ash/system/unified/notification_counter_view.h
@@ -8,31 +8,20 @@
 #include "ash/ash_export.h"
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/system/tray/tray_item_view.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "base/macros.h"
-#include "base/scoped_observation.h"
 
 namespace ash {
 
-class NotificationIconsController;
-class UnifiedSystemTray;
-
 // Maximum count of notification shown by a number label. "+" icon is shown
 // instead if it exceeds this limit.
 constexpr size_t kTrayNotificationMaxCount = 9;
 
-// A notification counter view in UnifiedSystemTray button. This will be shown
-// when there's notification and the tray doesn't show any notification icons.
-class ASH_EXPORT NotificationCounterView
-    : public TrayItemView,
-      public SessionObserver,
-      public UnifiedSystemTrayModel::Observer {
+// A notification counter view in UnifiedSystemTray button.
+class ASH_EXPORT NotificationCounterView : public TrayItemView,
+                                           public SessionObserver {
  public:
-  NotificationCounterView(UnifiedSystemTray* tray,
-                          NotificationIconsController* controller);
+  explicit NotificationCounterView(Shelf* shelf);
   ~NotificationCounterView() override;
-  NotificationCounterView(const NotificationCounterView&) = delete;
-  NotificationCounterView& operator=(const NotificationCounterView&) = delete;
 
   void Update();
 
@@ -45,10 +34,6 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
-  // UnifiedSystemTrayModel::Observer:
-  void OnSystemTrayButtonSizeChanged(
-      UnifiedSystemTrayModel::SystemTrayButtonSize system_tray_size) override;
-
   // views::TrayItemView:
   const char* GetClassName() const override;
 
@@ -61,30 +46,7 @@
   // |kTrayNotificationMaxCount| + 1 indicates the plus icon.
   int count_for_display_ = 0;
 
-  // Indicates if the notification icons view is set to be shown. Currently, we
-  // show the icon view in medium or large screen size.
-  bool icons_view_visible_ = false;
-
-  NotificationIconsController* const controller_;
-
-  base::ScopedObservation<UnifiedSystemTrayModel,
-                          UnifiedSystemTrayModel::Observer>
-      system_tray_model_observation_{this};
-};
-
-// An icon view to indicate the number of hidden notifications (besides from the
-// notifications that are shown in tray).
-class HiddenNotificationCountView : public TrayItemView {
- public:
-  explicit HiddenNotificationCountView(Shelf* shelf);
-  ~HiddenNotificationCountView() override;
-  HiddenNotificationCountView(const HiddenNotificationCountView&) = delete;
-  HiddenNotificationCountView& operator=(const HiddenNotificationCountView&) =
-      delete;
-
-  // TrayItemView:
-  void HandleLocaleChange() override;
-  const char* GetClassName() const override;
+  DISALLOW_COPY_AND_ASSIGN(NotificationCounterView);
 };
 
 // A do-not-distrub icon view in UnifiedSystemTray button.
@@ -92,8 +54,6 @@
  public:
   explicit QuietModeView(Shelf* shelf);
   ~QuietModeView() override;
-  QuietModeView(const QuietModeView&) = delete;
-  QuietModeView& operator=(const QuietModeView&) = delete;
 
   void Update();
 
@@ -105,6 +65,9 @@
 
   // views::TrayItemView:
   const char* GetClassName() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuietModeView);
 };
 
 }  // namespace ash
diff --git a/ash/system/unified/notification_counter_view_unittest.cc b/ash/system/unified/notification_counter_view_unittest.cc
index a6166c403..d598463 100644
--- a/ash/system/unified/notification_counter_view_unittest.cc
+++ b/ash/system/unified/notification_counter_view_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "ash/system/unified/notification_counter_view.h"
 
+#include "ash/media/media_notification_constants.h"
 #include "ash/public/cpp/ash_features.h"
-#include "ash/system/unified/notification_icons_controller.h"
-#include "ash/system/unified/unified_system_tray.h"
+#include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -21,8 +21,7 @@
 
 namespace ash {
 
-class NotificationCounterViewTest : public AshTestBase,
-                                    public testing::WithParamInterface<bool> {
+class NotificationCounterViewTest : public AshTestBase {
  public:
   NotificationCounterViewTest() = default;
   NotificationCounterViewTest(const NotificationCounterViewTest&) = delete;
@@ -33,46 +32,27 @@
   // AshTestBase:
   void SetUp() override {
     AshTestBase::SetUp();
-    scoped_feature_list_.InitWithFeatureState(features::kScalableStatusArea,
-                                              IsScalableStatusAreaEnabled());
 
-    tray_ = std::make_unique<UnifiedSystemTray>(GetPrimaryShelf());
-
-    if (IsScalableStatusAreaEnabled()) {
-      notification_icons_controller_ =
-          std::make_unique<NotificationIconsController>(tray_.get());
-      notification_icons_controller_->AddNotificationTrayItems(
-          tray_->tray_container());
-      notification_counter_view_ = std::make_unique<NotificationCounterView>(
-          tray_.get(), notification_icons_controller_.get());
-    } else {
-      notification_counter_view_ =
-          std::make_unique<NotificationCounterView>(tray_.get(), nullptr);
-    }
+    notification_counter_view_ =
+        std::make_unique<NotificationCounterView>(GetPrimaryShelf());
   }
 
-  bool IsScalableStatusAreaEnabled() { return GetParam(); }
-
   void TearDown() override {
     notification_counter_view_.reset();
-    notification_icons_controller_.reset();
-    tray_.reset();
     AshTestBase::TearDown();
   }
 
  protected:
   void AddNotification(const std::string& notification_id,
-                       bool is_pinned = false) {
-    message_center::RichNotificationData rich_notification_data;
-    rich_notification_data.pinned = is_pinned;
+                       const std::string& app_id = "app") {
     message_center::MessageCenter::Get()->AddNotification(
         std::make_unique<message_center::Notification>(
             message_center::NOTIFICATION_TYPE_BASE_FORMAT, notification_id,
             base::UTF8ToUTF16("test_title"), base::UTF8ToUTF16("test message"),
             gfx::Image(), /*display_source=*/base::string16(), GURL(),
             message_center::NotifierId(
-                message_center::NotifierType::APPLICATION, "app"),
-            rich_notification_data,
+                message_center::NotifierType::APPLICATION, app_id),
+            message_center::RichNotificationData(),
             new message_center::NotificationDelegate()));
   }
 
@@ -81,17 +61,13 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<UnifiedSystemTray> tray_;
-  std::unique_ptr<NotificationIconsController> notification_icons_controller_;
   std::unique_ptr<NotificationCounterView> notification_counter_view_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         NotificationCounterViewTest,
-                         testing::Bool() /* IsScalableStatusAreaEnabled() */);
+TEST_F(NotificationCounterViewTest, CountForDisplay) {
+  // VM camera/mic notifications are ignored by the counter.
+  AddNotification("camera & mic", kVmCameraMicNotifierId);
 
-TEST_P(NotificationCounterViewTest, CountForDisplay) {
   // Not visible when count == 0.
   notification_counter_view()->Update();
   EXPECT_EQ(0, notification_counter_view()->count_for_display_for_testing());
@@ -114,39 +90,27 @@
   EXPECT_TRUE(notification_counter_view()->GetVisible());
 }
 
-TEST_P(NotificationCounterViewTest, DisplayChanged) {
-  AddNotification("0", false /* is_pinned */);
-  AddNotification("1", true /* is_pinned */);
+// Media notifications are not included when flag is set.
+TEST_F(NotificationCounterViewTest, MediaNotifications) {
   notification_counter_view()->Update();
-
-  // In medium size screen, the counter should not be displayed since pinned
-  // notification icon is shown (if the feature is enabled).
-  UpdateDisplay("800x800");
-  EXPECT_EQ(IsScalableStatusAreaEnabled(),
-            !notification_counter_view()->GetVisible());
-
-  // The counter should be shown when we remove the pinned notification.
-  message_center::MessageCenter::Get()->RemoveNotification("1",
-                                                           false /* by_user */);
-  notification_counter_view()->Update();
-  EXPECT_TRUE(notification_counter_view()->GetVisible());
-
-  AddNotification("1", true /* is_pinned */);
-  notification_counter_view()->Update();
-
-  // In small display, the counter show be shown with pinned notification.
-  UpdateDisplay("600x600");
-  EXPECT_TRUE(notification_counter_view()->GetVisible());
-
-  // In large screen size, expected the same behavior like medium screen size.
-  UpdateDisplay("1680x800");
-  EXPECT_EQ(IsScalableStatusAreaEnabled(),
-            !notification_counter_view()->GetVisible());
-
-  message_center::MessageCenter::Get()->RemoveNotification("1",
-                                                           false /* by_user */);
-  notification_counter_view()->Update();
-  EXPECT_TRUE(notification_counter_view()->GetVisible());
+  EXPECT_EQ(0, notification_counter_view()->count_for_display_for_testing());
+  AddNotification("1", kMediaSessionNotifierId);
+  {
+    // Counter should ignore media notifications when feature is enabled.
+    base::test::ScopedFeatureList features;
+    features.InitAndEnableFeature(features::kMediaNotificationsCounter);
+    notification_counter_view()->Update();
+    EXPECT_EQ(0, notification_counter_view()->count_for_display_for_testing());
+    EXPECT_FALSE(notification_counter_view()->GetVisible());
+  }
+  {
+    // Counter should show media notifications when feature is disabled.
+    base::test::ScopedFeatureList features;
+    features.InitAndDisableFeature(features::kMediaNotificationsCounter);
+    notification_counter_view()->Update();
+    EXPECT_EQ(1, notification_counter_view()->count_for_display_for_testing());
+    EXPECT_TRUE(notification_counter_view()->GetVisible());
+  }
 }
 
 }  // namespace ash
diff --git a/ash/system/unified/notification_icons_controller.cc b/ash/system/unified/notification_icons_controller.cc
index af6c6fe..c441a5a 100644
--- a/ash/system/unified/notification_icons_controller.cc
+++ b/ash/system/unified/notification_icons_controller.cc
@@ -5,21 +5,16 @@
 #include "ash/system/unified/notification_icons_controller.h"
 
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/strings/grit/ash_strings.h"
 #include "ash/system/message_center/message_center_utils.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
-#include "ash/system/tray/tray_utils.h"
-#include "ash/system/unified/notification_counter_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_model.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/vector_icons.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/views/controls/separator.h"
 
 namespace ash {
 
@@ -27,7 +22,6 @@
 
 // Maximum number of notification icons shown in the system tray button.
 constexpr int kMaxNotificationIconsShown = 2;
-constexpr int kSeparatorPadding = 3;
 
 // We only show notification icon in the tray if it is either:
 // *   Pinned (generally used for background process such as sharing your
@@ -39,25 +33,6 @@
              message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
 }
 
-class SeparatorTrayItemView : public TrayItemView {
- public:
-  explicit SeparatorTrayItemView(Shelf* shelf) : TrayItemView(shelf) {
-    views::Separator* separator = new views::Separator();
-    separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
-        AshColorProvider::ContentLayerType::kSeparatorColor));
-    separator->SetBorder(
-        views::CreateEmptyBorder(gfx::Insets(kSeparatorPadding)));
-    AddChildView(separator);
-  }
-  ~SeparatorTrayItemView() override = default;
-  SeparatorTrayItemView(const SeparatorTrayItemView&) = delete;
-  SeparatorTrayItemView& operator=(const SeparatorTrayItemView&) = delete;
-
-  // TrayItemView:
-  void HandleLocaleChange() override {}
-  const char* GetClassName() const override { return "SeparatorTrayItemView"; }
-};
-
 }  // namespace
 
 NotificationIconTrayItemView::NotificationIconTrayItemView(Shelf* shelf)
@@ -133,44 +108,14 @@
         std::make_unique<NotificationIconTrayItemView>(tray_->shelf())));
   }
 
-  hidden_notification_count_view_ = tray_container->AddChildView(
-      std::make_unique<HiddenNotificationCountView>(tray_->shelf()));
-
-  separator_ = tray_container->AddChildView(
-      std::make_unique<SeparatorTrayItemView>(tray_->shelf()));
-
   OnSystemTrayButtonSizeChanged(tray_->model()->GetSystemTrayButtonSize());
 }
 
-void NotificationIconsController::UpdateHiddenNotificationCounter() {
-  if (!icons_view_visible_ || !TrayItemHasNotification()) {
-    hidden_notification_count_view_->SetVisible(false);
-    return;
-  }
-
-  // `first_unused_item_index_` is also the total number of notification icons
-  // shown in the tray.
-  int hidden_notification_num =
-      message_center_utils::GetNotificationCount() - first_unused_item_index_;
-  if (hidden_notification_num != 0)
-    hidden_notification_count_view_->label()->SetText(
-        l10n_util::GetStringFUTF16Int(
-            IDS_ASH_STATUS_TRAY_HIDDEN_NOTIFICATION_COUNT_LABEL,
-            hidden_notification_num));
-
-  hidden_notification_count_view_->SetVisible(hidden_notification_num != 0);
-}
-
-bool NotificationIconsController::TrayItemHasNotification() {
-  return first_unused_item_index_ != 0;
-}
-
 void NotificationIconsController::OnSystemTrayButtonSizeChanged(
     UnifiedSystemTrayModel::SystemTrayButtonSize system_tray_size) {
   icons_view_visible_ =
       system_tray_size != UnifiedSystemTrayModel::SystemTrayButtonSize::kSmall;
-  UpdateNotificationIcons();
-  UpdateHiddenNotificationCounter();
+  Update();
 }
 
 void NotificationIconsController::OnNotificationAdded(const std::string& id) {
@@ -181,7 +126,7 @@
 
   // Reset the notification icons if a notification is added since we don't
   // know the position where its icon should be added.
-  UpdateNotificationIcons();
+  Update();
 }
 
 void NotificationIconsController::OnNotificationRemoved(const std::string& id,
@@ -189,7 +134,7 @@
   // If the notification removed is displayed in an icon, call update to show
   // another notification if needed.
   if (GetNotificationIconShownInTray(id))
-    UpdateNotificationIcons();
+    Update();
 }
 
 void NotificationIconsController::OnNotificationUpdated(const std::string& id) {
@@ -198,7 +143,7 @@
     item->UpdateTooltipText();
 }
 
-void NotificationIconsController::UpdateNotificationIcons() {
+void NotificationIconsController::Update() {
   auto it = tray_items_.begin();
   for (message_center::Notification* notification :
        message_center_utils::GetSortedVisibleNotifications()) {
@@ -217,7 +162,6 @@
     (*it)->Reset();
     (*it)->SetVisible(false);
   }
-  separator_->SetVisible(icons_view_visible_ && TrayItemHasNotification());
 }
 
 NotificationIconTrayItemView*
diff --git a/ash/system/unified/notification_icons_controller.h b/ash/system/unified/notification_icons_controller.h
index 33acae70..05532827 100644
--- a/ash/system/unified/notification_icons_controller.h
+++ b/ash/system/unified/notification_icons_controller.h
@@ -75,12 +75,6 @@
   // Initialize the view by adding items to the container of the tray.
   void AddNotificationTrayItems(TrayContainer* tray_container);
 
-  // Update the text and visibility of the hidden notification counter.
-  void UpdateHiddenNotificationCounter();
-
-  // Returns true if any item in `tray_items_` is containing a notification.
-  bool TrayItemHasNotification();
-
   // UnifiedSystemTrayModel::Observer:
   void OnSystemTrayButtonSizeChanged(
       UnifiedSystemTrayModel::SystemTrayButtonSize system_tray_size) override;
@@ -95,11 +89,8 @@
   }
 
  private:
-  friend class NotificationIconsControllerTest;
-
-  // Iterate through the notifications in message center and update the icons
-  // shown accordingly.
-  void UpdateNotificationIcons();
+  // Update the icons shown according to the notifications in message center.
+  void Update();
 
   // If the notification with given id is currently shown in tray, returns the
   // pointer to that tray item. Otherwise, returns a null pointer.
@@ -114,15 +105,11 @@
   // icons tray item. All the items in previous index are used and visible.
   size_t first_unused_item_index_ = 0;
 
-  // Indicates if the notification icons view is set to be shown. Currently, we
-  // show the icon view in medium or large screen size.
+  // Indicates if the notification icons view is set to be shown.
   bool icons_view_visible_ = false;
 
   UnifiedSystemTray* tray_;
 
-  TrayItemView* hidden_notification_count_view_ = nullptr;
-  TrayItemView* separator_ = nullptr;
-
   base::ScopedObservation<UnifiedSystemTrayModel,
                           UnifiedSystemTrayModel::Observer>
       system_tray_model_observation_{this};
diff --git a/ash/system/unified/notification_icons_controller_unittest.cc b/ash/system/unified/notification_icons_controller_unittest.cc
index 5ff8704..950ba38 100644
--- a/ash/system/unified/notification_icons_controller_unittest.cc
+++ b/ash/system/unified/notification_icons_controller_unittest.cc
@@ -4,14 +4,10 @@
 
 #include "ash/system/unified/notification_icons_controller.h"
 
-#include "ash/media/media_notification_constants.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/notification_utils.h"
-#include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ash/system/tray/tray_item_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 
@@ -38,17 +34,7 @@
     AshTestBase::TearDown();
   }
 
-  TrayItemView* separator() {
-    return notification_icons_controller_->separator_;
-  }
-
-  TrayItemView* hidden_notification_count_view() {
-    return notification_icons_controller_->hidden_notification_count_view_;
-  }
-
-  std::string AddNotification(bool is_pinned,
-                              bool is_critical_warning,
-                              const std::string& app_id = "app") {
+  std::string AddNotification(bool is_pinned, bool is_critical_warning) {
     std::string id = base::NumberToString(notification_id_++);
 
     auto warning_level =
@@ -64,7 +50,7 @@
             base::UTF8ToUTF16("test_title"), base::UTF8ToUTF16("test message"),
             base::string16() /*display_source */, GURL() /* origin_url */,
             message_center::NotifierId(
-                message_center::NotifierType::SYSTEM_COMPONENT, app_id),
+                message_center::NotifierType::SYSTEM_COMPONENT, "ash.debug"),
             rich_notification_data, nullptr /* delegate */, gfx::VectorIcon(),
             warning_level));
     notification_id_++;
@@ -80,29 +66,20 @@
 
 TEST_F(NotificationIconsControllerTest, DisplayChanged) {
   AddNotification(true /* is_pinned */, false /* is_critical_warning */);
-  AddNotification(false /* is_pinned */, false /* is_critical_warning */);
-  notification_icons_controller_->UpdateHiddenNotificationCounter();
-
-  // Notification icons should be shown in medium screen size.
-  UpdateDisplay("800x800");
-  EXPECT_TRUE(
-      notification_icons_controller_->tray_items().front()->GetVisible());
-  EXPECT_TRUE(separator()->GetVisible());
-  EXPECT_TRUE(hidden_notification_count_view()->GetVisible());
 
   // Notification icons should not be shown in small screen size.
   UpdateDisplay("600x600");
   EXPECT_FALSE(
       notification_icons_controller_->tray_items().front()->GetVisible());
-  EXPECT_FALSE(separator()->GetVisible());
-  EXPECT_FALSE(hidden_notification_count_view()->GetVisible());
 
-  // Notification icons should be shown in large screen size.
+  // Notification icons should be shown in medium and large screen size.
+  UpdateDisplay("800x800");
+  EXPECT_TRUE(
+      notification_icons_controller_->tray_items().front()->GetVisible());
+
   UpdateDisplay("1680x800");
   EXPECT_TRUE(
       notification_icons_controller_->tray_items().front()->GetVisible());
-  EXPECT_TRUE(separator()->GetVisible());
-  EXPECT_TRUE(hidden_notification_count_view()->GetVisible());
 }
 
 TEST_F(NotificationIconsControllerTest, ShowNotificationIcons) {
@@ -111,13 +88,11 @@
   // If there's no notification, no notification icons should be shown.
   EXPECT_FALSE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_FALSE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_FALSE(separator()->GetVisible());
 
   // Same case for non pinned or non critical warning notification.
   AddNotification(false /* is_pinned */, false /* is_critical_warning */);
   EXPECT_FALSE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_FALSE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_FALSE(separator()->GetVisible());
 
   // Notification icons should be shown when pinned or critical warning
   // notification is added.
@@ -125,13 +100,11 @@
       AddNotification(true /* is_pinned */, false /* is_critical_warning */);
   EXPECT_TRUE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_FALSE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_TRUE(separator()->GetVisible());
 
   std::string id1 =
       AddNotification(false /* is_pinned */, true /* is_critical_warning */);
   EXPECT_TRUE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_TRUE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_TRUE(separator()->GetVisible());
 
   // Remove the critical warning notification should make the tray show only one
   // icon.
@@ -139,47 +112,12 @@
                                                            false /* by_user */);
   EXPECT_TRUE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_FALSE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_TRUE(separator()->GetVisible());
 
   // Remove the pinned notification, no icon is shown.
   message_center::MessageCenter::Get()->RemoveNotification(id0,
                                                            false /* by_user */);
   EXPECT_FALSE(notification_icons_controller_->tray_items()[0]->GetVisible());
   EXPECT_FALSE(notification_icons_controller_->tray_items()[1]->GetVisible());
-  EXPECT_FALSE(separator()->GetVisible());
-}
-
-TEST_F(NotificationIconsControllerTest, HiddenNotificationCount) {
-  UpdateDisplay("800x800");
-
-  // If there's no notification, the counter should be hidden by default.
-  EXPECT_FALSE(hidden_notification_count_view()->GetVisible());
-
-  int hidden_notification_num = 5;
-  base::string16 expected_text = base::UTF8ToUTF16("+5");
-
-  // The counter should not be shown if no icon is displayed in the tray (a.k.a
-  // no important notification).
-  for (int i = 0; i < hidden_notification_num; ++i) {
-    AddNotification(false /* is_pinned */, false /* is_critical_warning */);
-  }
-  notification_icons_controller_->UpdateHiddenNotificationCounter();
-  EXPECT_FALSE(hidden_notification_count_view()->GetVisible());
-
-  // Added a pinned notification, the counter should now be shown with the
-  // expected text.
-  std::string id0 =
-      AddNotification(true /* is_pinned */, false /* is_critical_warning */);
-  notification_icons_controller_->UpdateHiddenNotificationCounter();
-  EXPECT_TRUE(hidden_notification_count_view()->GetVisible());
-  EXPECT_EQ(expected_text,
-            hidden_notification_count_view()->label()->GetText());
-
-  // Remove the pinned notification should make the counter switch to hidden.
-  message_center::MessageCenter::Get()->RemoveNotification(id0,
-                                                           false /* by_user */);
-  notification_icons_controller_->UpdateHiddenNotificationCounter();
-  EXPECT_FALSE(hidden_notification_count_view()->GetVisible());
 }
 
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 092a7c3a..b2ac971 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -137,9 +137,7 @@
                                     CameraMicTrayItemView::Type::kCamera)),
       mic_view_(
           new CameraMicTrayItemView(shelf, CameraMicTrayItemView::Type::kMic)),
-      notification_counter_item_(
-          new NotificationCounterView(this,
-                                      notification_icons_controller_.get())),
+      notification_counter_item_(new NotificationCounterView(shelf)),
       quiet_mode_view_(new QuietModeView(shelf)),
       time_view_(new tray::TimeTrayItemView(shelf, model())) {
   tray_container()->SetMargin(
@@ -495,8 +493,6 @@
 void UnifiedSystemTray::UpdateNotificationAfterDelay() {
   notification_counter_item_->Update();
   quiet_mode_view_->Update();
-  if (notification_icons_controller_)
-    notification_icons_controller_->UpdateHiddenNotificationCounter();
 }
 
 message_center::MessagePopupView*
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index e2220263..ad2e516a 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -5,8 +5,10 @@
 #include "ash/system/unified/unified_system_tray_controller.h"
 
 #include "ash/capture_mode/capture_mode_feature_pod_controller.h"
+#include "ash/constants/ash_features.h"
 #include "ash/metrics/user_metrics_action.h"
 #include "ash/metrics/user_metrics_recorder.h"
+#include "ash/projector/projector_feature_pod_controller.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
@@ -438,9 +440,10 @@
   AddFeaturePodItem(std::make_unique<AccessibilityFeaturePodController>(this));
   AddFeaturePodItem(std::make_unique<QuietModeFeaturePodController>(this));
   AddFeaturePodItem(std::make_unique<RotationLockFeaturePodController>());
-  AddFeaturePodItem(std::make_unique<PrivacyScreenFeaturePodController>());
   if (features::IsCaptureModeEnabled())
     AddFeaturePodItem(std::make_unique<CaptureModeFeaturePodController>(this));
+  if (chromeos::features::IsProjectorFeaturePodEnabled())
+    AddFeaturePodItem(std::make_unique<ProjectorFeaturePodController>(this));
   AddFeaturePodItem(std::make_unique<NearbyShareFeaturePodController>(this));
   AddFeaturePodItem(std::make_unique<NightLightFeaturePodController>(this));
   AddFeaturePodItem(std::make_unique<CastFeaturePodController>(this));
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index f96f8c9..c50f730 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -239,7 +239,8 @@
     NotifyContentChanged();
 
   // Update the window's workspace to this parent desk.
-  if (features::IsBentoEnabled() && !is_desk_being_removed_) {
+  if ((features::IsBentoEnabled() || features::IsFullRestoreEnabled()) &&
+      !is_desk_being_removed_) {
     auto* desks_controller = DesksController::Get();
     window->SetProperty(aura::client::kWindowWorkspaceKey,
                         desks_controller->GetDeskIndex(this));
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc
index 8a9cc957..6dd53b2 100644
--- a/ash/wm/desks/desk_preview_view.cc
+++ b/ash/wm/desks/desk_preview_view.cc
@@ -376,7 +376,8 @@
     // them in the layer tree if |this| is not the preview view for the active
     // desk.
     visible_on_all_desks_windows_to_mirror =
-        Shell::Get()->desks_controller()->visible_on_all_desks_windows();
+        Shell::Get()->desks_controller()->GetVisibleOnAllDesksWindowsOnRoot(
+            mini_view_->root_window());
     for (auto* window : visible_on_all_desks_windows_to_mirror)
       GetLayersData(window, &layers_data);
   }
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 2731ad9..eceeac0 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -264,6 +264,18 @@
   return active_desk();
 }
 
+base::flat_set<aura::Window*>
+DesksController::GetVisibleOnAllDesksWindowsOnRoot(
+    aura::Window* root_window) const {
+  DCHECK(root_window->IsRootWindow());
+  base::flat_set<aura::Window*> filtered_visible_on_all_desks_windows;
+  for (auto* visible_on_all_desks_window : visible_on_all_desks_windows_) {
+    if (visible_on_all_desks_window->GetRootWindow() == root_window)
+      filtered_visible_on_all_desks_windows.insert(visible_on_all_desks_window);
+  }
+  return filtered_visible_on_all_desks_windows;
+}
+
 void DesksController::RestorePrimaryUserActiveDeskIndex(int active_desk_index) {
   DCHECK_GE(active_desk_index, 0);
   DCHECK_LT(active_desk_index, int{desks_.size()});
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 6d6a669..c14a619 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -90,6 +90,10 @@
   // switch animation is in progress.
   const Desk* GetTargetActiveDesk() const;
 
+  // Returns the visible on all desks windows that reside on |root_window|.
+  base::flat_set<aura::Window*> GetVisibleOnAllDesksWindowsOnRoot(
+      aura::Window* root_window) const;
+
   // Restores the primary user's activate desk at active_desk_index.
   void RestorePrimaryUserActiveDeskIndex(int active_desk_index);
 
diff --git a/ash/wm/full_restore/full_restore_controller.cc b/ash/wm/full_restore/full_restore_controller.cc
index df63d95..86810883 100644
--- a/ash/wm/full_restore/full_restore_controller.cc
+++ b/ash/wm/full_restore/full_restore_controller.cc
@@ -4,10 +4,15 @@
 
 #include "ash/wm/full_restore/full_restore_controller.h"
 
+#include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
-#include "ash/wm/full_restore/full_restore_window_manager.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/window_state.h"
 #include "base/check_op.h"
+#include "components/full_restore/full_restore_utils.h"
 #include "components/prefs/pref_service.h"
+#include "ui/aura/client/aura_constants.h"
 
 namespace ash {
 
@@ -15,11 +20,33 @@
 
 FullRestoreController* g_instance = nullptr;
 
+// Callback for testing which is run when SaveWindowImpl triggers a write to
+// file.
+FullRestoreController::SaveWindowCallback g_save_window_callback_for_testing;
+
+// The list of possible app window parents.
+// TODO(crbug.com/1164472): Support the rest of the desk containers which
+// are currently not always created depending on whether the bento feature
+// is enabled.
+constexpr ShellWindowId kAppParentContainers[5] = {
+    kShellWindowId_DefaultContainerDeprecated,
+    kShellWindowId_DeskContainerB,
+    kShellWindowId_DeskContainerC,
+    kShellWindowId_DeskContainerD,
+    kShellWindowId_AlwaysOnTopContainer,
+};
+
+// The types of apps currently supported by full restore.
+// TODO(crbug.com/1164472): Add ARC apps when they are supported.
+// TODO(crbug.com/1164472): Checking app type is temporary solution until we
+// can get windows which are allowed to full restore from the
+// FullRestoreService.
+constexpr AppType kSupportedAppTypes[2] = {AppType::BROWSER,
+                                           AppType::CHROME_APP};
+
 }  // namespace
 
-FullRestoreController::FullRestoreController()
-    : full_restore_window_manager_(
-          std::make_unique<FullRestoreWindowManager>()) {
+FullRestoreController::FullRestoreController() {
   DCHECK_EQ(nullptr, g_instance);
   g_instance = this;
 
@@ -33,32 +60,97 @@
 
 // static
 FullRestoreController* FullRestoreController::Get() {
-  DCHECK(g_instance);
   return g_instance;
 }
 
-void FullRestoreController::SaveWindows() {
-  ++save_windows_count_for_testing_;
+void FullRestoreController::SaveWindow(WindowState* window_state) {
+  SaveWindowImpl(window_state, /*activation_index=*/base::nullopt);
+}
 
-  // TODO(crbug.com/1164472): Hook up to full_restore::FullRestoreSaveHandler.
+void FullRestoreController::SaveAllWindows() {
+  auto windows =
+      Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks);
+  for (int i = 0; i < int{windows.size()}; ++i) {
+    WindowState* window_state = WindowState::Get(windows[i]);
+
+    // Flip the index so that larger values are associated with more recently
+    // used windows. See SaveWindowImpl for more details.
+    const int activation_index = windows.size() - 1 - i;
+    SaveWindowImpl(window_state, activation_index);
+  }
 }
 
 void FullRestoreController::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
-  // TODO(crbug.com/1164472): Register and the check the pref service and call
-  // FullRestoreWindowManager::SetEnabled accordingly.
+  // TODO(crbug.com/1164472): Register and the check the pref service.
 }
 
 void FullRestoreController::OnTabletModeStarted() {
-  SaveWindows();
+  SaveAllWindows();
 }
 
 void FullRestoreController::OnTabletModeEnded() {
-  SaveWindows();
+  SaveAllWindows();
 }
 
 void FullRestoreController::OnTabletControllerDestroyed() {
   tablet_mode_observeration_.Reset();
 }
 
+void FullRestoreController::SaveWindowImpl(
+    WindowState* window_state,
+    base::Optional<int> activation_index) {
+  DCHECK(window_state);
+  aura::Window* window = window_state->window();
+
+  if (!base::Contains(kAppParentContainers, window->parent()->id()))
+    return;
+
+  // Only some app types can be saved.
+  if (!base::Contains(
+          kSupportedAppTypes,
+          static_cast<AppType>(window->GetProperty(aura::client::kAppType)))) {
+    return;
+  }
+
+  int window_activation_index;
+  if (activation_index) {
+    window_activation_index = *activation_index;
+  } else {
+    // The returned MRU list has the more recently used windows have a smaller
+    // index. We want to flip it so that larger values are associated with
+    // more recently used windows instead. This is because when creating
+    // windows, we will add them into a aura::Window hierarichy, whose
+    // children are stacked such that the highest indexed children are stacked
+    // closest to the top. We reverse the order here since we have access to
+    // the full MRU list, as opposed to on read when we will not have enough
+    // info when the first couple of windows get added. Due to there being
+    // equal or more windows in the MRU list than app windows, the activation
+    // indexes may not be consecutive, but the relative order will be correct.
+    auto windows =
+        Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks);
+    std::reverse(windows.begin(), windows.end());
+    auto it = std::find(windows.begin(), windows.end(), window);
+    if (it != windows.end())
+      window_activation_index = it - windows.begin();
+  }
+
+  full_restore::WindowInfo window_info;
+  window_info.activation_index = window_activation_index;
+  window_info.window = window;
+  window_info.desk_id = window->GetProperty(aura::client::kWindowWorkspaceKey);
+  window_info.restore_bounds = window_state->GetRestoreBoundsInScreen();
+  window_info.current_bounds = window->GetBoundsInScreen();
+  window_info.window_state_type = window_state->GetStateType();
+  full_restore::SaveWindowInfo(window_info);
+
+  if (g_save_window_callback_for_testing)
+    g_save_window_callback_for_testing.Run(window_info);
+}
+
+void FullRestoreController::SetSaveWindowCallbackForTesting(
+    SaveWindowCallback callback) {
+  g_save_window_callback_for_testing = std::move(callback);
+}
+
 }  // namespace ash
diff --git a/ash/wm/full_restore/full_restore_controller.h b/ash/wm/full_restore/full_restore_controller.h
index eb21a083..72cbaad 100644
--- a/ash/wm/full_restore/full_restore_controller.h
+++ b/ash/wm/full_restore/full_restore_controller.h
@@ -10,16 +10,20 @@
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/scoped_observation.h"
+#include "components/full_restore/window_info.h"
 
 class PrefService;
 
 namespace ash {
 
-class FullRestoreWindowManager;
+class WindowState;
 
 class ASH_EXPORT FullRestoreController : public SessionObserver,
                                          public TabletModeObserver {
  public:
+  using SaveWindowCallback =
+      base::RepeatingCallback<void(const full_restore::WindowInfo&)>;
+
   FullRestoreController();
   FullRestoreController(const FullRestoreController&) = delete;
   FullRestoreController& operator=(const FullRestoreController&) = delete;
@@ -29,10 +33,12 @@
   // Shell.
   static FullRestoreController* Get();
 
-  // Calls full_restore::FullRestoreSaveHandler to save to the database. The
-  // handler has timer to prevent too many writes, but we should limit calls
-  // regardless if possible.
-  void SaveWindows();
+  // Calls SaveWindowImpl for |window_state|. The activation index will be
+  // calculated in SaveWindowImpl.
+  void SaveWindow(WindowState* window_state);
+
+  // Saves all windows in the MRU window tracker.
+  void SaveAllWindows();
 
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
@@ -45,10 +51,19 @@
  private:
   friend class FullRestoreControllerTest;
 
-  // Tracks how many times SaveWindows has been called.
-  int save_windows_count_for_testing_ = 0;
+  // Calls full_restore::FullRestoreSaveHandler to save to file. The handler has
+  // timer to prevent too many writes, but we should limit calls regardless if
+  // possible. Optionally passes |activation_index|, which is calculated with
+  // respect to the MRU tracker. Calling SaveAllWindows will iterate through
+  // the MRU tracker list, so we can pass the activation index during that loop
+  // instead of building the MRU list again for each window.
+  void SaveWindowImpl(WindowState* window_state,
+                      base::Optional<int> activation_index);
 
-  std::unique_ptr<FullRestoreWindowManager> full_restore_window_manager_;
+  // Sets a callback for testing that will be fired immediately when
+  // SaveWindowImpl is about to notify the full restore component we want to
+  // write to file.
+  void SetSaveWindowCallbackForTesting(SaveWindowCallback callback);
 
   base::ScopedObservation<TabletModeController, TabletModeObserver>
       tablet_mode_observeration_{this};
diff --git a/ash/wm/full_restore/full_restore_controller_unittest.cc b/ash/wm/full_restore/full_restore_controller_unittest.cc
index 88cd71f..fe51456 100644
--- a/ash/wm/full_restore/full_restore_controller_unittest.cc
+++ b/ash/wm/full_restore/full_restore_controller_unittest.cc
@@ -11,7 +11,9 @@
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
+#include "base/containers/flat_map.h"
 #include "base/test/scoped_feature_list.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
@@ -27,58 +29,82 @@
 
 class FullRestoreControllerTest : public AshTestBase {
  public:
+  // Struct which is the data in our fake full restore file.
+  struct WindowInfo {
+    int call_count = 0;
+    int activation_index = 0;
+  };
+
   FullRestoreControllerTest() = default;
   FullRestoreControllerTest(const FullRestoreControllerTest&) = delete;
   FullRestoreControllerTest& operator=(const FullRestoreControllerTest&) =
       delete;
   ~FullRestoreControllerTest() override = default;
 
-  int GetSaveWindowsCount() const {
-    return FullRestoreController::Get()->save_windows_count_for_testing_;
+  // Returns the number of times |window| has been saved to file since the last
+  // ResetSaveWindowsCount call.
+  int GetSaveWindowsCount(aura::Window* window) const {
+    if (!base::Contains(fake_full_restore_file_, window))
+      return 0;
+    return fake_full_restore_file_.at(window).call_count;
+  }
+
+  // Returns the total number of saves since the last ResetSaveWindowsCount
+  // call.
+  int GetTotalSaveWindowsCount() const {
+    int count = 0;
+    for (const std::pair<aura::Window*, WindowInfo>& member :
+         fake_full_restore_file_) {
+      count += member.second.call_count;
+    }
+    return count;
   }
 
   void ResetSaveWindowsCount() {
-    FullRestoreController::Get()->save_windows_count_for_testing_ = 0;
+    for (std::pair<aura::Window*, WindowInfo>& member : fake_full_restore_file_)
+      member.second.call_count = 0;
+  }
+
+  // Returns the stored activation index for |window|.
+  int GetActivationIndex(aura::Window* window) const {
+    if (!base::Contains(fake_full_restore_file_, window))
+      return -1;
+    return fake_full_restore_file_.at(window).activation_index;
   }
 
   // AshTestBase:
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(features::kFullRestore);
+
     AshTestBase::SetUp();
+
+    FullRestoreController::Get()->SetSaveWindowCallbackForTesting(
+        base::BindRepeating(&FullRestoreControllerTest::OnSaveWindow,
+                            base::Unretained(this)));
   }
 
  private:
+  // Called when FullRestoreController saves a window to the file. Immediately
+  // writes to our fake file |fake_full_restore_file_|.
+  void OnSaveWindow(const full_restore::WindowInfo& window_info) {
+    aura::Window* window = window_info.window;
+    DCHECK(window_info.activation_index);
+
+    if (fake_full_restore_file_.contains(window)) {
+      fake_full_restore_file_[window].call_count++;
+      fake_full_restore_file_[window].activation_index =
+          *window_info.activation_index;
+    } else {
+      fake_full_restore_file_[window] = {0, *window_info.activation_index};
+    }
+  }
+
+  // A map which is a fake representation of the full restore file.
+  base::flat_map<aura::Window*, WindowInfo> fake_full_restore_file_;
+
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Tests that data gets saved when an application window is created and the data
-// gets removed when the application is closed.
-TEST_F(FullRestoreControllerTest, AppWindowAddedClosed) {
-  const gfx::Rect bounds(200, 200);
-  auto browser_window = CreateAppWindow(bounds, AppType::BROWSER);
-  auto chrome_app_window = CreateAppWindow(bounds, AppType::CHROME_APP);
-
-  // For now, creating a window will trigger two saves, one when adding the
-  // window to its parent and one when the window gets its initial bounds set.
-  // The actual writing to the database is throttled, so this is ok.
-  EXPECT_EQ(4, GetSaveWindowsCount());
-
-  // Tests we save each time a viable app window is destroyed.
-  browser_window.reset();
-  chrome_app_window.reset();
-  EXPECT_EQ(6, GetSaveWindowsCount());
-
-  // Test that creating and destroying a system app writes nothing to the
-  // database.
-  // TODO(crbug.com/1164472): Checking app type is temporary solution until we
-  // can get windows which are allowed to full restore from the
-  // FullRestoreService.
-  ResetSaveWindowsCount();
-  auto system_window = CreateAppWindow(bounds, AppType::SYSTEM_APP);
-  system_window.reset();
-  EXPECT_EQ(0, GetSaveWindowsCount());
-}
-
 // Tests that data gets saved when changing a window's window state.
 TEST_F(FullRestoreControllerTest, WindowStateChanged) {
   auto window = CreateAppWindow(gfx::Rect(600, 600), AppType::BROWSER);
@@ -86,23 +112,25 @@
 
   auto* window_state = WindowState::Get(window.get());
   window_state->Minimize();
-  EXPECT_EQ(1, GetSaveWindowsCount());
+  EXPECT_EQ(1, GetSaveWindowsCount(window.get()));
 
   window_state->Unminimize();
-  EXPECT_EQ(2, GetSaveWindowsCount());
+  EXPECT_EQ(2, GetSaveWindowsCount(window.get()));
+
+  window_state->Activate();
+  EXPECT_EQ(3, GetSaveWindowsCount(window.get()));
 
   // Maximize and restore will invoke two calls to SaveWindows because
   // their animations also change the bounds of the window. The actual writing
   // to the database is throttled, so this is ok.
-  window_state->Activate();
   window_state->Maximize();
-  EXPECT_EQ(4, GetSaveWindowsCount());
+  EXPECT_EQ(5, GetSaveWindowsCount(window.get()));
 
   window_state->Restore();
-  EXPECT_EQ(6, GetSaveWindowsCount());
+  EXPECT_EQ(7, GetSaveWindowsCount(window.get()));
 
   PerformAcceleratorAction(WINDOW_CYCLE_SNAP_LEFT, {});
-  EXPECT_EQ(7, GetSaveWindowsCount());
+  EXPECT_EQ(8, GetSaveWindowsCount(window.get()));
 }
 
 // Tests that data gets saved when moving a window to another desk.
@@ -122,7 +150,7 @@
       DESKS_MOVE_ACTIVE_ITEM,
       {ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN});
   ASSERT_NE(previous_parent, window->parent());
-  EXPECT_EQ(1, GetSaveWindowsCount());
+  EXPECT_EQ(1, GetSaveWindowsCount(window.get()));
 }
 
 // Tests that data gets saved when moving a window to another display using the
@@ -138,7 +166,7 @@
   PerformAcceleratorAction(MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS, {});
   ASSERT_TRUE(
       gfx::Rect(801, 0, 800, 800).Contains(window->GetBoundsInScreen()));
-  EXPECT_EQ(1, GetSaveWindowsCount());
+  EXPECT_EQ(1, GetSaveWindowsCount(window.get()));
 }
 
 // Tests that data gets saved when dragging a window.
@@ -146,24 +174,43 @@
   auto window = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
   ResetSaveWindowsCount();
 
+  // Test that even if we move n times, we will only save to file once.
   const gfx::Point point_on_frame(200, 16);
   auto* event_generator = GetEventGenerator();
   event_generator->set_current_screen_location(point_on_frame);
   event_generator->PressLeftButton();
-  event_generator->MoveMouseBy(15, 15);
-  event_generator->MoveMouseBy(15, 15);
-  event_generator->MoveMouseBy(15, 15);
+  for (int i = 0; i < 5; ++i)
+    event_generator->MoveMouseBy(15, 15);
   event_generator->ReleaseLeftButton();
 
-  EXPECT_EQ(3, GetSaveWindowsCount());
+  EXPECT_EQ(1, GetSaveWindowsCount(window.get()));
 }
 
 TEST_F(FullRestoreControllerTest, TabletModeChange) {
+  // Tests that with no windows, nothing gets save when entering or exiting
+  // tablet mode.
   TabletModeControllerTestApi().EnterTabletMode();
-  EXPECT_EQ(1, GetSaveWindowsCount());
+  EXPECT_EQ(0, GetTotalSaveWindowsCount());
 
   TabletModeControllerTestApi().LeaveTabletMode();
-  EXPECT_EQ(2, GetSaveWindowsCount());
+  EXPECT_EQ(0, GetTotalSaveWindowsCount());
+
+  auto window1 = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
+  auto window2 = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
+  ResetSaveWindowsCount();
+
+  // Tests that we save each window when entering or exiting tablet mode. Due to
+  // many possible things changing during a tablet switch (window state, bounds,
+  // etc.), we cannot determine exactly how many saves there will be, but there
+  // should be more than one per window.
+  TabletModeControllerTestApi().EnterTabletMode();
+  EXPECT_GT(GetSaveWindowsCount(window1.get()), 1);
+  EXPECT_GT(GetSaveWindowsCount(window2.get()), 1);
+
+  ResetSaveWindowsCount();
+  TabletModeControllerTestApi().LeaveTabletMode();
+  EXPECT_GT(GetSaveWindowsCount(window1.get()), 1);
+  EXPECT_GT(GetSaveWindowsCount(window2.get()), 1);
 }
 
 TEST_F(FullRestoreControllerTest, DisplayAddRemove) {
@@ -179,17 +226,37 @@
   display::ManagedDisplayInfo second_info =
       display_manager()->GetDisplayInfo(second_id);
 
-  // Remove the secondary display.
+  // Remove the secondary display. Doing so will change both the bounds of the
+  // window and activate it, resulting in a double save.
   std::vector<display::ManagedDisplayInfo> display_info_list;
   display_info_list.push_back(primary_info);
   display_manager()->OnNativeDisplaysChanged(display_info_list);
-  EXPECT_EQ(1, GetSaveWindowsCount());
+  EXPECT_EQ(2, GetSaveWindowsCount(window.get()));
 
   // Reconnect the secondary display. PersistentWindowController will move the
   // window back to the secondary display, so a save should be triggered.
   display_info_list.push_back(second_info);
   display_manager()->OnNativeDisplaysChanged(display_info_list);
-  EXPECT_EQ(2, GetSaveWindowsCount());
+  EXPECT_EQ(3, GetSaveWindowsCount(window.get()));
+}
+
+TEST_F(FullRestoreControllerTest, Activation) {
+  auto window1 = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
+  auto window2 = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
+  auto window3 = CreateAppWindow(gfx::Rect(400, 400), AppType::BROWSER);
+  ResetSaveWindowsCount();
+
+  // Tests that an activation will save once for each window.
+  wm::ActivateWindow(window1.get());
+  EXPECT_EQ(3, GetTotalSaveWindowsCount());
+
+  // Tests that most recently used windows have the highest activation index.
+  wm::ActivateWindow(window1.get());
+  wm::ActivateWindow(window2.get());
+  wm::ActivateWindow(window3.get());
+  EXPECT_EQ(0, GetActivationIndex(window1.get()));
+  EXPECT_EQ(1, GetActivationIndex(window2.get()));
+  EXPECT_EQ(2, GetActivationIndex(window3.get()));
 }
 
 }  // namespace ash
diff --git a/ash/wm/full_restore/full_restore_window_manager.cc b/ash/wm/full_restore/full_restore_window_manager.cc
deleted file mode 100644
index f3d57dc..0000000
--- a/ash/wm/full_restore/full_restore_window_manager.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/wm/full_restore/full_restore_window_manager.h"
-
-#include "ash/public/cpp/app_types.h"
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ash/shell.h"
-#include "ash/wm/full_restore/full_restore_controller.h"
-#include "ui/aura/client/aura_constants.h"
-
-namespace ash {
-
-namespace {
-
-// The list of possible app window parents.
-// TODO(crbug.com/1164472): Support the rest of the desk containers which
-// are currently not always created depending on whether the bento feature
-// is enabled.
-constexpr ShellWindowId kAppParentContainers[5] = {
-    kShellWindowId_DefaultContainerDeprecated,
-    kShellWindowId_DeskContainerB,
-    kShellWindowId_DeskContainerC,
-    kShellWindowId_DeskContainerD,
-    kShellWindowId_AlwaysOnTopContainer,
-};
-
-// The types of apps currently supported by full restore.
-// TODO(crbug.com/1164472): Add ARC apps when they are supported.
-// TODO(crbug.com/1164472): Checking app type is temporary solution until we
-// can get windows which are allowed to full restore from the
-// FullRestoreService.
-constexpr AppType kSupportedAppTypes[2] = {AppType::BROWSER,
-                                           AppType::CHROME_APP};
-
-}  // namespace
-
-FullRestoreWindowManager::FullRestoreWindowManager() {
-  // TODO(crbug.com/1164472): SetEnabled should be called from
-  // FullRestoreController. For now this is ok as the feature is disabled by
-  // default.
-  SetEnabled(true);
-}
-
-FullRestoreWindowManager::~FullRestoreWindowManager() {
-  StopObserving();
-}
-
-void FullRestoreWindowManager::SetEnabled(bool enabled) {
-  if (enabled_ == enabled)
-    return;
-
-  enabled_ = enabled;
-
-  if (!enabled) {
-    // TODO(crbug.com/1164472): Clear database.
-    StopObserving();
-    return;
-  }
-
-  DCHECK(!app_window_parents_observations_.IsObservingAnySource());
-  for (aura::Window* root_window : Shell::GetAllRootWindows()) {
-    for (ShellWindowId id : kAppParentContainers) {
-      aura::Window* child = root_window->GetChildById(id);
-      DCHECK(child);
-      app_window_parents_observations_.AddObservation(child);
-    }
-  }
-
-  // TODO(crbug.com/1164472): Start observing and save existing app windows in
-  // the MRU list to the database.
-}
-
-void FullRestoreWindowManager::OnWindowAdded(aura::Window* new_window) {
-  DCHECK(app_window_parents_observations_.IsObservingAnySource());
-  DCHECK(new_window->parent());
-
-  if (!app_window_parents_observations_.IsObservingSource(new_window->parent()))
-    return;
-
-  // TODO(crbug.com/1164472): For browser and chrome apps, the window property
-  // is set after the window is created. Change those apps to set the property
-  // using Widget's |init_properties_container| so the property can be extracted
-  // at this time.
-  if (!base::Contains(kSupportedAppTypes,
-                      static_cast<AppType>(
-                          new_window->GetProperty(aura::client::kAppType)))) {
-    return;
-  }
-
-  // The window is already observed if it is switching containers (i.e. moving
-  // window between desks).
-  if (!app_window_observations_.IsObservingSource(new_window)) {
-    app_window_observations_.AddObservation(new_window);
-
-    auto* new_window_state = WindowState::Get(new_window);
-    DCHECK(!app_window_state_observations_.IsObservingSource(new_window_state));
-    app_window_state_observations_.AddObservation(new_window_state);
-  }
-
-  FullRestoreController::Get()->SaveWindows();
-}
-
-void FullRestoreWindowManager::OnWindowDestroying(aura::Window* window) {
-  // Do nothing if windows in |app_window_parents_| are destroyed. |this| will
-  // be destroyed shortly and their observers will be cleaned up then.
-  if (app_window_parents_observations_.IsObservingSource(window)) {
-    app_window_parents_observations_.RemoveObservation(window);
-    return;
-  }
-
-  auto* window_state = WindowState::Get(window);
-  DCHECK(app_window_observations_.IsObservingSource(window));
-  DCHECK(app_window_state_observations_.IsObservingSource(window_state));
-
-  app_window_observations_.RemoveObservation(window);
-  app_window_state_observations_.RemoveObservation(window_state);
-
-  FullRestoreController::Get()->SaveWindows();
-}
-
-void FullRestoreWindowManager::OnWindowBoundsChanged(
-    aura::Window* window,
-    const gfx::Rect& old_bounds,
-    const gfx::Rect& new_bounds,
-    ui::PropertyChangeReason reason) {
-  if (!app_window_observations_.IsObservingSource(window))
-    return;
-
-  if (reason == ui::PropertyChangeReason::FROM_ANIMATION)
-    return;
-
-  // TODO(crbug.com/1164472): Investigate if we can skip bounds change updates
-  // that happen during a window drag.
-
-  FullRestoreController::Get()->SaveWindows();
-}
-
-void FullRestoreWindowManager::OnWindowRemovingFromRootWindow(
-    aura::Window* window,
-    aura::Window* new_root) {
-  if (!app_window_observations_.IsObservingSource(window))
-    return;
-
-  FullRestoreController::Get()->SaveWindows();
-}
-
-void FullRestoreWindowManager::OnPostWindowStateTypeChange(
-    WindowState* window_state,
-    chromeos::WindowStateType old_type) {
-  DCHECK(app_window_state_observations_.IsObservingSource(window_state));
-
-  // TODO(crbug.com/1164472): We may not be interested in all window state
-  // changes.
-
-  FullRestoreController::Get()->SaveWindows();
-}
-
-void FullRestoreWindowManager::StopObserving() {
-  app_window_parents_observations_.RemoveAllObservations();
-  app_window_observations_.RemoveAllObservations();
-  app_window_state_observations_.RemoveAllObservations();
-}
-
-}  // namespace ash
diff --git a/ash/wm/full_restore/full_restore_window_manager.h b/ash/wm/full_restore/full_restore_window_manager.h
deleted file mode 100644
index c8e5d66..0000000
--- a/ash/wm/full_restore/full_restore_window_manager.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WM_FULL_RESTORE_FULL_RESTORE_WINDOW_MANAGER_H_
-#define ASH_WM_FULL_RESTORE_FULL_RESTORE_WINDOW_MANAGER_H_
-
-#include "ash/ash_export.h"
-#include "ash/wm/window_state.h"
-#include "ash/wm/window_state_observer.h"
-#include "base/scoped_multi_source_observation.h"
-#include "chromeos/ui/base/window_state_type.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace ash {
-
-// Observes windows that are can be full restored and notifies
-// FullRestoreController to write to the database when interesting changes are
-// made to the windows.
-class FullRestoreWindowManager : public aura::WindowObserver,
-                                 public WindowStateObserver {
- public:
-  FullRestoreWindowManager();
-  FullRestoreWindowManager(const FullRestoreWindowManager&) = delete;
-  FullRestoreWindowManager& operator=(const FullRestoreWindowManager&) = delete;
-  ~FullRestoreWindowManager() override;
-
-  // Called when the user turns full restore on or off via the os setting.
-  // Starts observing and notifies FullRestoreService to save all current app
-  // windows to the database if |enabled| is true. Stops observing and notifies
-  // FullRestoreService to remove all current app windows from the database if
-  // |enabled| is false.
-  void SetEnabled(bool enabled);
-
-  // aura::WindowObserver:
-  void OnWindowAdded(aura::Window* new_window) override;
-  void OnWindowDestroying(aura::Window* window) override;
-  void OnWindowBoundsChanged(aura::Window* window,
-                             const gfx::Rect& old_bounds,
-                             const gfx::Rect& new_bounds,
-                             ui::PropertyChangeReason reason) override;
-  void OnWindowRemovingFromRootWindow(aura::Window* window,
-                                      aura::Window* new_root) override;
-
-  // WindowStateObserver:
-  void OnPostWindowStateTypeChange(WindowState* window_state,
-                                   chromeos::WindowStateType old_type) override;
-
- private:
-  void StopObserving();
-
-  // If false, we do not observe any windows since the user chose to not use the
-  // full restore feature.
-  bool enabled_ = false;
-
-  // The windows that can be the parent of an app windows. This includes the
-  // desk containers and the always on top container. All windows in this set
-  // are observed for when a child gets added or removed. Empty if the full
-  // restore setting is disabled.
-  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
-      app_window_parents_observations_{this};
-
-  // The app windows we are currently observing. Empty if the full restore
-  // setting is disabled.
-  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
-      app_window_observations_{this};
-
-  // The app window states we are currently observing. This should match the
-  // size of |app_window_observations_|.
-  // TODO(crbug.com/1164472): We may just want to call
-  // FullRestoreController::SaveWindows in WindowState..
-  base::ScopedMultiSourceObservation<WindowState, WindowStateObserver>
-      app_window_state_observations_{this};
-};
-
-}  // namespace ash
-
-#endif  // ASH_WM_FULL_RESTORE_FULL_RESTORE_WINDOW_MANAGER_H_
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc
index 456fa4e..79559a3 100644
--- a/ash/wm/mru_window_tracker.cc
+++ b/ash/wm/mru_window_tracker.cc
@@ -7,12 +7,14 @@
 #include <algorithm>
 
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/ash_focus_rules.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/full_restore/full_restore_controller.h"
 #include "ash/wm/switchable_windows.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -289,8 +291,13 @@
 void MruWindowTracker::OnWindowActivated(ActivationReason reason,
                                          aura::Window* gained_active,
                                          aura::Window* lost_active) {
-  if (!ignore_window_activations_)
-    SetActiveWindow(gained_active);
+  if (ignore_window_activations_)
+    return;
+
+  SetActiveWindow(gained_active);
+
+  if (gained_active && features::IsFullRestoreEnabled())
+    FullRestoreController::Get()->SaveAllWindows();
 }
 
 void MruWindowTracker::OnWindowDestroyed(aura::Window* window) {
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 26373ef..faf5ddc 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -10,6 +10,7 @@
 #include "ash/focus_cycler.h"
 #include "ash/metrics/pip_uma.h"
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_animation_types.h"
 #include "ash/public/cpp/window_properties.h"
@@ -17,6 +18,7 @@
 #include "ash/shell.h"
 #include "ash/wm/collision_detection/collision_detection_utils.h"
 #include "ash/wm/default_state.h"
+#include "ash/wm/full_restore/full_restore_controller.h"
 #include "ash/wm/pip/pip_positioner.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_animations.h"
@@ -175,6 +177,16 @@
                              base::TimeDelta::FromHours(10), 50);
 }
 
+// Notifies the full restore controller to write to file.
+void SaveWindowForFullRestore(WindowState* window_state) {
+  if (!features::IsFullRestoreEnabled())
+    return;
+
+  auto* controller = FullRestoreController::Get();
+  if (controller)
+    controller->SaveWindow(window_state);
+}
+
 }  // namespace
 
 constexpr base::TimeDelta WindowState::kBoundsChangeSlideDuration;
@@ -524,6 +536,7 @@
   DCHECK(drag_details_);
   if (delegate_)
     delegate_->OnDragFinished(/*canceled=*/false, location);
+  SaveWindowForFullRestore(this);
 }
 
 void WindowState::OnRevertDrag(const gfx::PointF& location) {
@@ -668,6 +681,7 @@
   for (auto& observer : observer_list_)
     observer.OnPostWindowStateTypeChange(this, old_window_state_type);
   OnPostPipStateChange(old_window_state_type);
+  SaveWindowForFullRestore(this);
 }
 
 void WindowState::OnPostPipStateChange(WindowStateType old_window_state_type) {
@@ -910,6 +924,16 @@
     }
     return;
   }
+  if (key == aura::client::kWindowWorkspaceKey) {
+    // Save the window for full restore purposes unless
+    // |ignore_property_change_| is true. Note that moving windows across
+    // displays will also trigger a property change, even if the value stays the
+    // same, so we do not need to save the window when it changes root windows
+    // (OnWindowAddedToRootWindow).
+    if (!ignore_property_change_)
+      SaveWindowForFullRestore(this);
+    return;
+  }
 
   // The shelf visibility should be updated if kHideShelfWhenFullscreenKey or
   // kImmersiveIsActive change - these property affect the shelf behavior, and
@@ -957,6 +981,9 @@
       window_->GetProperty(ash::kWindowManagerManagesOpacityKey)) {
     window_->SetOpaqueRegionsForOcclusion({gfx::Rect(new_bounds.size())});
   }
+
+  if (reason != ui::PropertyChangeReason::FROM_ANIMATION && !is_dragged())
+    SaveWindowForFullRestore(this);
 }
 
 }  // namespace ash
diff --git a/base/allocator/partition_allocator/address_pool_manager.cc b/base/allocator/partition_allocator/address_pool_manager.cc
index 8ca8a3e..673967bc 100644
--- a/base/allocator/partition_allocator/address_pool_manager.cc
+++ b/base/allocator/partition_allocator/address_pool_manager.cc
@@ -265,14 +265,14 @@
                                   size_t length) {
   uintptr_t ptr_as_uintptr = reinterpret_cast<uintptr_t>(address);
   AutoLock guard(AddressPoolManagerBitmap::GetLock());
-  if (handle == kNonBRPPoolHandle) {
-    SetBitmap(AddressPoolManagerBitmap::non_brp_pool_bits_,
+  if (handle == kDirectMapHandle) {
+    SetBitmap(AddressPoolManagerBitmap::directmap_bits_,
               ptr_as_uintptr / PageAllocationGranularity(),
               length / PageAllocationGranularity());
   } else {
-    PA_DCHECK(handle == kBRPPoolHandle);
+    PA_DCHECK(handle == kNormalBucketHandle);
     PA_DCHECK(!(length & kSuperPageOffsetMask));
-    SetBitmap(AddressPoolManagerBitmap::brp_pool_bits_,
+    SetBitmap(AddressPoolManagerBitmap::normal_bucket_bits_,
               ptr_as_uintptr >> kSuperPageShift, length >> kSuperPageShift);
   }
 }
@@ -281,25 +281,24 @@
                                     uintptr_t address,
                                     size_t length) {
   AutoLock guard(AddressPoolManagerBitmap::GetLock());
-  // Currently, address regions allocated by kBRPPoolHandle are never freed
-  // in PartitionAlloc, because only normal buckets are allocated from there.
-  // Thus we have LIKELY for kNonBRPPoolHandle
-  if (LIKELY(handle == kNonBRPPoolHandle)) {
-    ResetBitmap(AddressPoolManagerBitmap::non_brp_pool_bits_,
+  // Currently, address regions allocated by kNormalBucketHandle are never freed
+  // in PartitionAlloc. Thus we have LIKELY for kDirectMapHandle
+  if (LIKELY(handle == kDirectMapHandle)) {
+    ResetBitmap(AddressPoolManagerBitmap::directmap_bits_,
                 address / PageAllocationGranularity(),
                 length / PageAllocationGranularity());
   } else {
-    PA_DCHECK(handle == kBRPPoolHandle);
+    PA_DCHECK(handle == kNormalBucketHandle);
     PA_DCHECK(!(length & kSuperPageOffsetMask));
-    ResetBitmap(AddressPoolManagerBitmap::brp_pool_bits_,
+    ResetBitmap(AddressPoolManagerBitmap::normal_bucket_bits_,
                 address >> kSuperPageShift, length >> kSuperPageShift);
   }
 }
 
 void AddressPoolManager::ResetForTesting() {
   AutoLock guard(AddressPoolManagerBitmap::GetLock());
-  AddressPoolManagerBitmap::non_brp_pool_bits_.reset();
-  AddressPoolManagerBitmap::brp_pool_bits_.reset();
+  AddressPoolManagerBitmap::directmap_bits_.reset();
+  AddressPoolManagerBitmap::normal_bucket_bits_.reset();
 }
 
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h
index 2802acb..95188d1 100644
--- a/base/allocator/partition_allocator/address_pool_manager.h
+++ b/base/allocator/partition_allocator/address_pool_manager.h
@@ -32,15 +32,14 @@
 //
 // (32bit version)
 // AddressPoolManager wraps AllocPages and FreePages and remembers allocated
-// address regions using bitmaps. IsManagedByPartitionAllocNonBRPPool and
-// IsManagedByPartitionAllocBRPPool use the bitmaps to judge whether a given
-// address is in a pool that supports BackupRefPtr or in a pool that doesn't.
-// All PartitionAlloc allocations must be in either of the pools.
+// address regions using bitmaps. IsManagedByPartitionAllocDirectMap and
+// IsManagedByPartitionAllocNormalBuckets use the bitmaps to judge whether a
+// given address is managed by the direct map or normal buckets.
 class BASE_EXPORT AddressPoolManager {
   static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull;
 
  public:
-  static constexpr uint64_t kBRPPoolMaxSize =
+  static constexpr uint64_t kNormalBucketMaxSize =
 #if defined(PA_HAS_64_BITS_POINTERS)
       16 * kGiB;
 #else
@@ -60,12 +59,12 @@
   void ResetForTesting();
 
 #if !defined(PA_HAS_64_BITS_POINTERS)
-  static bool IsManagedByNonBRPPool(const void* address) {
-    return AddressPoolManagerBitmap::IsManagedByNonBRPPool(address);
+  static bool IsManagedByDirectMapPool(const void* address) {
+    return AddressPoolManagerBitmap::IsManagedByDirectMapPool(address);
   }
 
-  static bool IsManagedByBRPPool(const void* address) {
-    return AddressPoolManagerBitmap::IsManagedByBRPPool(address);
+  static bool IsManagedByNormalBucketPool(const void* address) {
+    return AddressPoolManagerBitmap::IsManagedByNormalBucketPool(address);
   }
 #endif
 
@@ -89,7 +88,7 @@
    private:
     // The bitset stores the allocation state of the address pool. 1 bit per
     // super-page: 1 = allocated, 0 = free.
-    static constexpr size_t kMaxBits = kBRPPoolMaxSize / kSuperPageSize;
+    static constexpr size_t kMaxBits = kNormalBucketMaxSize / kSuperPageSize;
 
     base::Lock lock_;
     std::bitset<kMaxBits> alloc_bitset_ GUARDED_BY(lock_);
@@ -119,12 +118,10 @@
   void MarkUsed(pool_handle handle, const char* address, size_t size);
   void MarkUnused(pool_handle handle, uintptr_t address, size_t size);
 
-  // BRP stands for BackupRefPtr. GigaCage is split into pools, one which
-  // supports BackupRefPtr and one that doesn't.
-  static constexpr pool_handle kNonBRPPoolHandle = 1;
-  static constexpr pool_handle kBRPPoolHandle = 2;
-  friend internal::pool_handle GetNonBRPPool();
-  friend internal::pool_handle GetBRPPool();
+  static constexpr pool_handle kDirectMapHandle = 1;
+  static constexpr pool_handle kNormalBucketHandle = 2;
+  friend internal::pool_handle GetDirectMapPool();
+  friend internal::pool_handle GetNormalBucketPool();
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
 
   friend struct base::LazyInstanceTraitsBase<AddressPoolManager>;
@@ -132,12 +129,12 @@
 };
 
 #if !defined(PA_HAS_64_BITS_POINTERS)
-ALWAYS_INLINE internal::pool_handle GetNonBRPPool() {
-  return AddressPoolManager::kNonBRPPoolHandle;
+ALWAYS_INLINE internal::pool_handle GetDirectMapPool() {
+  return AddressPoolManager::kDirectMapHandle;
 }
 
-ALWAYS_INLINE internal::pool_handle GetBRPPool() {
-  return AddressPoolManager::kBRPPoolHandle;
+ALWAYS_INLINE internal::pool_handle GetNormalBucketPool() {
+  return AddressPoolManager::kNormalBucketHandle;
 }
 #endif
 
diff --git a/base/allocator/partition_allocator/address_pool_manager_bitmap.cc b/base/allocator/partition_allocator/address_pool_manager_bitmap.cc
index af8f1c6b..6f0c002 100644
--- a/base/allocator/partition_allocator/address_pool_manager_bitmap.cc
+++ b/base/allocator/partition_allocator/address_pool_manager_bitmap.cc
@@ -21,11 +21,12 @@
   return g_lock.Get();
 }
 
-std::bitset<AddressPoolManagerBitmap::kNonBRPPoolBits>
-    AddressPoolManagerBitmap::non_brp_pool_bits_;  // GUARDED_BY(GetLock())
-std::bitset<AddressPoolManagerBitmap::kBRPPoolBits>
-    AddressPoolManagerBitmap::brp_pool_bits_;  // GUARDED_BY(GetLock())
+std::bitset<AddressPoolManagerBitmap::kDirectMapBits>
+    AddressPoolManagerBitmap::directmap_bits_;  // GUARDED_BY(GetLock())
+std::bitset<AddressPoolManagerBitmap::kNormalBucketBits>
+    AddressPoolManagerBitmap::normal_bucket_bits_;  // GUARDED_BY(GetLock())
 
 }  // namespace internal
 }  // namespace base
+
 #endif  // !defined(PA_HAS_64_BITS_POINTERS)
diff --git a/base/allocator/partition_allocator/address_pool_manager_bitmap.h b/base/allocator/partition_allocator/address_pool_manager_bitmap.h
index 6cff449..8448c5e 100644
--- a/base/allocator/partition_allocator/address_pool_manager_bitmap.h
+++ b/base/allocator/partition_allocator/address_pool_manager_bitmap.h
@@ -16,37 +16,32 @@
 
 namespace internal {
 
-// AddressPoolManagerBitmap is a set of bitmaps that track whether a given
-// address is in a pool that supports BackupRefPtr, or in a pool that doesn't
-// support it. All PartitionAlloc allocations must be in either of the pools.
-//
-// This code is specific to 32-bit systems.
+// AddressPoolManagerBitmap is the bitmap that tracks whether a given address is
+// managed by the direct map or normal buckets.
 class BASE_EXPORT AddressPoolManagerBitmap {
  public:
   static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull;
   static constexpr uint64_t kAddressSpaceSize = 4ull * kGiB;
-  // Non-BRP pool includes, among others, direct map allocations, which reserve
-  // address space at PageAllocationGranularity(). BRP pool only supports normal
-  // bucket allocations, which always reserve address space at 2MB granularity.
-  static constexpr size_t kNonBRPPoolBits =
+  static constexpr size_t kNormalBucketBits =
+      kAddressSpaceSize / kSuperPageSize;
+  static constexpr size_t kDirectMapBits =
       kAddressSpaceSize / PageAllocationGranularity();
-  static constexpr size_t kBRPPoolBits = kAddressSpaceSize / kSuperPageSize;
 
-  static bool IsManagedByNonBRPPool(const void* address) {
+  static bool IsManagedByDirectMapPool(const void* address) {
     uintptr_t address_as_uintptr = reinterpret_cast<uintptr_t>(address);
-    // It is safe to read |non_brp_pool_bits_| without a lock since the caller
-    // is responsible for guaranteeing that the address is inside a valid
+    // It is safe to read |directmap_bits_| without a lock since the caller is
+    // responsible for guaranteeing that the address is inside a valid
     // allocation and the deallocation call won't race with this call.
-    return TS_UNCHECKED_READ(non_brp_pool_bits_)
+    return TS_UNCHECKED_READ(directmap_bits_)
         .test(address_as_uintptr / PageAllocationGranularity());
   }
 
-  static bool IsManagedByBRPPool(const void* address) {
+  static bool IsManagedByNormalBucketPool(const void* address) {
     uintptr_t address_as_uintptr = reinterpret_cast<uintptr_t>(address);
-    // It is safe to read |brp_pool_bits_| without a lock since the caller
+    // It is safe to read |normal_bucket_bits_| without a lock since the caller
     // is responsible for guaranteeing that the address is inside a valid
     // allocation and the deallocation call won't race with this call.
-    return TS_UNCHECKED_READ(brp_pool_bits_)
+    return TS_UNCHECKED_READ(normal_bucket_bits_)
         .test(address_as_uintptr >> kSuperPageShift);
   }
 
@@ -55,18 +50,20 @@
 
   static Lock& GetLock();
 
-  static std::bitset<kNonBRPPoolBits> non_brp_pool_bits_ GUARDED_BY(GetLock());
-  static std::bitset<kBRPPoolBits> brp_pool_bits_ GUARDED_BY(GetLock());
+  static std::bitset<kDirectMapBits> directmap_bits_ GUARDED_BY(GetLock());
+  static std::bitset<kNormalBucketBits> normal_bucket_bits_
+      GUARDED_BY(GetLock());
 };
 
 }  // namespace internal
 
-ALWAYS_INLINE bool IsManagedByPartitionAllocNonBRPPool(const void* address) {
-  return internal::AddressPoolManagerBitmap::IsManagedByNonBRPPool(address);
+ALWAYS_INLINE bool IsManagedByPartitionAllocDirectMap(const void* address) {
+  return internal::AddressPoolManagerBitmap::IsManagedByDirectMapPool(address);
 }
 
-ALWAYS_INLINE bool IsManagedByPartitionAllocBRPPool(const void* address) {
-  return internal::AddressPoolManagerBitmap::IsManagedByBRPPool(address);
+ALWAYS_INLINE bool IsManagedByPartitionAllocNormalBuckets(const void* address) {
+  return internal::AddressPoolManagerBitmap::IsManagedByNormalBucketPool(
+      address);
 }
 
 }  // namespace base
diff --git a/base/allocator/partition_allocator/address_pool_manager_unittest.cc b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
index b7d0d8c..f8d90c7 100644
--- a/base/allocator/partition_allocator/address_pool_manager_unittest.cc
+++ b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
@@ -168,13 +168,14 @@
 
 #else   // defined(PA_HAS_64_BITS_POINTERS)
 
-TEST_F(AddressPoolManagerTest, IsManagedByNonBRPPool) {
+TEST_F(AddressPoolManagerTest, IsManagedByDirectMapPool) {
   constexpr size_t kAllocCount = 8;
   static const size_t kNumPages[kAllocCount] = {1, 4, 7, 8, 13, 16, 31, 60};
   void* addrs[kAllocCount];
   for (size_t i = 0; i < kAllocCount; ++i) {
     addrs[i] = AddressPoolManager::GetInstance()->Reserve(
-        GetNonBRPPool(), nullptr, PageAllocationGranularity() * kNumPages[i]);
+        GetDirectMapPool(), nullptr,
+        PageAllocationGranularity() * kNumPages[i]);
     EXPECT_TRUE(addrs[i]);
     EXPECT_TRUE(
         !(reinterpret_cast<uintptr_t>(addrs[i]) & kSuperPageOffsetMask));
@@ -186,30 +187,31 @@
                        PageAllocationGranularity();
     for (size_t j = 0; j < num_pages; ++j) {
       if (j < kNumPages[i]) {
-        EXPECT_TRUE(AddressPoolManager::IsManagedByNonBRPPool(ptr));
+        EXPECT_TRUE(AddressPoolManager::IsManagedByDirectMapPool(ptr));
       } else {
-        EXPECT_FALSE(AddressPoolManager::IsManagedByNonBRPPool(ptr));
+        EXPECT_FALSE(AddressPoolManager::IsManagedByDirectMapPool(ptr));
       }
-      EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(ptr));
+      EXPECT_FALSE(AddressPoolManager::IsManagedByNormalBucketPool(ptr));
       ptr += PageAllocationGranularity();
     }
   }
   for (size_t i = 0; i < kAllocCount; ++i) {
     AddressPoolManager::GetInstance()->UnreserveAndDecommit(
-        GetNonBRPPool(), addrs[i], PageAllocationGranularity() * kNumPages[i]);
-    EXPECT_FALSE(AddressPoolManager::IsManagedByNonBRPPool(addrs[i]));
-    EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i]));
+        GetDirectMapPool(), addrs[i],
+        PageAllocationGranularity() * kNumPages[i]);
+    EXPECT_FALSE(AddressPoolManager::IsManagedByDirectMapPool(addrs[i]));
+    EXPECT_FALSE(AddressPoolManager::IsManagedByNormalBucketPool(addrs[i]));
   }
 }
 
-TEST_F(AddressPoolManagerTest, IsManagedByBRPPool) {
+TEST_F(AddressPoolManagerTest, IsManagedByNormalBucketPool) {
   constexpr size_t kAllocCount = 4;
   // Totally (1+3+7+11) * 2MB = 44MB allocation
   static const size_t kNumPages[kAllocCount] = {1, 3, 7, 11};
   void* addrs[kAllocCount];
   for (size_t i = 0; i < kAllocCount; ++i) {
     addrs[i] = AddressPoolManager::GetInstance()->Reserve(
-        GetBRPPool(), nullptr, kSuperPageSize * kNumPages[i]);
+        GetNormalBucketPool(), nullptr, kSuperPageSize * kNumPages[i]);
     EXPECT_TRUE(addrs[i]);
     EXPECT_TRUE(
         !(reinterpret_cast<uintptr_t>(addrs[i]) & kSuperPageOffsetMask));
@@ -218,16 +220,16 @@
     const char* ptr = reinterpret_cast<const char*>(addrs[i]);
     size_t num_system_pages = kNumPages[i] * kSuperPageSize / SystemPageSize();
     for (size_t j = 0; j < num_system_pages; ++j) {
-      EXPECT_TRUE(AddressPoolManager::IsManagedByBRPPool(ptr));
-      EXPECT_FALSE(AddressPoolManager::IsManagedByNonBRPPool(ptr));
+      EXPECT_TRUE(AddressPoolManager::IsManagedByNormalBucketPool(ptr));
+      EXPECT_FALSE(AddressPoolManager::IsManagedByDirectMapPool(ptr));
       ptr += SystemPageSize();
     }
   }
   for (size_t i = 0; i < kAllocCount; ++i) {
     AddressPoolManager::GetInstance()->UnreserveAndDecommit(
-        GetBRPPool(), addrs[i], kSuperPageSize * kNumPages[i]);
-    EXPECT_FALSE(AddressPoolManager::IsManagedByNonBRPPool(addrs[i]));
-    EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i]));
+        GetNormalBucketPool(), addrs[i], kSuperPageSize * kNumPages[i]);
+    EXPECT_FALSE(AddressPoolManager::IsManagedByDirectMapPool(addrs[i]));
+    EXPECT_FALSE(AddressPoolManager::IsManagedByNormalBucketPool(addrs[i]));
   }
 }
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index 2d7f311c..61bf1d8b 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -22,12 +22,13 @@
 // reserved address space. Therefore, set *_pool_base_address_ initially to
 // k*PoolOffsetMask, so that PartitionAddressSpace::IsIn*Pool() always returns
 // false.
-uintptr_t PartitionAddressSpace::non_brp_pool_base_address_ =
-    kNonBRPPoolOffsetMask;
-uintptr_t PartitionAddressSpace::brp_pool_base_address_ = kBRPPoolOffsetMask;
+uintptr_t PartitionAddressSpace::direct_map_pool_base_address_ =
+    kDirectMapPoolOffsetMask;
+uintptr_t PartitionAddressSpace::normal_bucket_pool_base_address_ =
+    kNormalBucketPoolOffsetMask;
 
-pool_handle PartitionAddressSpace::non_brp_pool_ = 0;
-pool_handle PartitionAddressSpace::brp_pool_ = 0;
+pool_handle PartitionAddressSpace::direct_map_pool_ = 0;
+pool_handle PartitionAddressSpace::normal_bucket_pool_ = 0;
 
 void PartitionAddressSpace::Init() {
   if (IsInitialized())
@@ -40,25 +41,25 @@
 
   uintptr_t current = reserved_base_address_;
 
-  non_brp_pool_base_address_ = current;
-  non_brp_pool_ = internal::AddressPoolManager::GetInstance()->Add(
-      current, kNonBRPPoolSize);
-  PA_DCHECK(non_brp_pool_);
-  PA_DCHECK(!IsInNonBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(IsInNonBRPPool(reinterpret_cast<void*>(current)));
-  current += kNonBRPPoolSize;
-  PA_DCHECK(IsInNonBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(!IsInNonBRPPool(reinterpret_cast<void*>(current)));
+  direct_map_pool_base_address_ = current;
+  direct_map_pool_ = internal::AddressPoolManager::GetInstance()->Add(
+      current, kDirectMapPoolSize);
+  PA_DCHECK(direct_map_pool_);
+  PA_DCHECK(!IsInDirectMapPool(reinterpret_cast<void*>(current - 1)));
+  PA_DCHECK(IsInDirectMapPool(reinterpret_cast<void*>(current)));
+  current += kDirectMapPoolSize;
+  PA_DCHECK(IsInDirectMapPool(reinterpret_cast<void*>(current - 1)));
+  PA_DCHECK(!IsInDirectMapPool(reinterpret_cast<void*>(current)));
 
-  brp_pool_base_address_ = current;
-  brp_pool_ =
-      internal::AddressPoolManager::GetInstance()->Add(current, kBRPPoolSize);
-  PA_DCHECK(brp_pool_);
-  PA_DCHECK(!IsInBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(current)));
-  current += kBRPPoolSize;
-  PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(!IsInBRPPool(reinterpret_cast<void*>(current)));
+  normal_bucket_pool_base_address_ = current;
+  normal_bucket_pool_ = internal::AddressPoolManager::GetInstance()->Add(
+      current, kNormalBucketPoolSize);
+  PA_DCHECK(normal_bucket_pool_);
+  PA_DCHECK(!IsInNormalBucketPool(reinterpret_cast<void*>(current - 1)));
+  PA_DCHECK(IsInNormalBucketPool(reinterpret_cast<void*>(current)));
+  current += kNormalBucketPoolSize;
+  PA_DCHECK(IsInNormalBucketPool(reinterpret_cast<void*>(current - 1)));
+  PA_DCHECK(!IsInNormalBucketPool(reinterpret_cast<void*>(current)));
 
   PA_DCHECK(reserved_base_address_ + kDesiredAddressSpaceSize == current);
 }
@@ -67,10 +68,10 @@
   FreePages(reinterpret_cast<void*>(reserved_base_address_),
             kReservedAddressSpaceAlignment);
   reserved_base_address_ = 0;
-  non_brp_pool_base_address_ = kNonBRPPoolOffsetMask;
-  brp_pool_base_address_ = kBRPPoolOffsetMask;
-  non_brp_pool_ = 0;
-  brp_pool_ = 0;
+  direct_map_pool_base_address_ = kDirectMapPoolOffsetMask;
+  normal_bucket_pool_base_address_ = kNormalBucketPoolOffsetMask;
+  direct_map_pool_ = 0;
+  normal_bucket_pool_ = 0;
   internal::AddressPoolManager::GetInstance()->ResetForTesting();
 }
 
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h
index 75604d6..5795f5d 100644
--- a/base/allocator/partition_allocator/partition_address_space.h
+++ b/base/allocator/partition_allocator/partition_address_space.h
@@ -28,15 +28,15 @@
 // Reserves address space for PartitionAllocator.
 class BASE_EXPORT PartitionAddressSpace {
  public:
-  // BRP stands for BackupRefPtr. GigaCage is split into pools, one which
-  // supports BackupRefPtr and one that doesn't.
-  static ALWAYS_INLINE internal::pool_handle GetNonBRPPool() {
-    return non_brp_pool_;
+  static ALWAYS_INLINE constexpr uintptr_t NormalBucketPoolBaseMask() {
+    return kNormalBucketPoolBaseMask;
   }
-  static ALWAYS_INLINE internal::pool_handle GetBRPPool() { return brp_pool_; }
 
-  static ALWAYS_INLINE constexpr uintptr_t BRPPoolBaseMask() {
-    return kBRPPoolBaseMask;
+  static ALWAYS_INLINE internal::pool_handle GetDirectMapPool() {
+    return direct_map_pool_;
+  }
+  static ALWAYS_INLINE internal::pool_handle GetNormalBucketPool() {
+    return normal_bucket_pool_;
   }
 
   static void Init();
@@ -44,27 +44,27 @@
 
   static ALWAYS_INLINE bool IsInitialized() {
     if (reserved_base_address_) {
-      PA_DCHECK(non_brp_pool_ != 0);
-      PA_DCHECK(brp_pool_ != 0);
+      PA_DCHECK(direct_map_pool_ != 0);
+      PA_DCHECK(normal_bucket_pool_ != 0);
       return true;
     }
 
-    PA_DCHECK(non_brp_pool_ == 0);
-    PA_DCHECK(brp_pool_ == 0);
+    PA_DCHECK(direct_map_pool_ == 0);
+    PA_DCHECK(normal_bucket_pool_ == 0);
     return false;
   }
 
-  static ALWAYS_INLINE bool IsInNonBRPPool(const void* address) {
-    return (reinterpret_cast<uintptr_t>(address) & kNonBRPPoolBaseMask) ==
-           non_brp_pool_base_address_;
+  static ALWAYS_INLINE bool IsInDirectMapPool(const void* address) {
+    return (reinterpret_cast<uintptr_t>(address) & kDirectMapPoolBaseMask) ==
+           direct_map_pool_base_address_;
   }
-  static ALWAYS_INLINE bool IsInBRPPool(const void* address) {
-    return (reinterpret_cast<uintptr_t>(address) & kBRPPoolBaseMask) ==
-           brp_pool_base_address_;
+  static ALWAYS_INLINE bool IsInNormalBucketPool(const void* address) {
+    return (reinterpret_cast<uintptr_t>(address) & kNormalBucketPoolBaseMask) ==
+           normal_bucket_pool_base_address_;
   }
 
-  static ALWAYS_INLINE uintptr_t BRPPoolBase() {
-    return brp_pool_base_address_;
+  static ALWAYS_INLINE uintptr_t NormalBucketPoolBase() {
+    return normal_bucket_pool_base_address_;
   }
 
   // PartitionAddressSpace is static_only class.
@@ -75,8 +75,8 @@
 
  private:
   // Partition Alloc Address Space
-  // Reserves 32GiB address space for one pool that supports BackupRefPtr and
-  // one that doesn't, 16GiB each.
+  // Reserves 32GiB address space for one direct map pool and one normal bucket
+  // pool, 16GiB each.
   // TODO(bartekn): Look into devices with 39-bit address space that have 256GiB
   // user-mode space. Libraries loaded at random addresses may stand in the way
   // of reserving a contiguous 48GiB region (even though we're requesting only
@@ -84,19 +84,18 @@
   // alignment requirements).
   //
   // +----------------+ reserved_base_address_ (16GiB aligned)
-  // |    non-BRP     |     == non_brp_pool_base_address_
+  // |   direct map   |     == direct_map_pool_base_address_
   // |      pool      |
   // +----------------+ reserved_base_address_ + 16GiB
-  // |      BRP       |     == brp_pool_base_address_
+  // | normal bucket  |     == normal_bucket_pool_base_address_
   // |      pool      |
   // +----------------+ reserved_base_address_ + 32GiB
   //
-  // NOTE! On 64-bit systems with BackupRefPtr enabled, the non-BRP pool must
-  // precede the BRP pool. This is to prevent a pointer immediately past a
-  // non-GigaCage allocation from falling into the BRP pool, thus triggering
-  // BackupRefPtr mechanism and likely crashing.
-  // TODO(bartekn): Add a guard page at the end of direct map allocations to
-  // prevent pointers immediately past those from falling into the BRP pool.
+  // NOTE! On 64-bit systems with BackupRefPtr enabled, the direct map pool must
+  // precede normal bucket pool. This is to prevent a pointer immediately past a
+  // non-GigaCage allocation from falling into the normal bucket pool, thus
+  // triggering BackupRefPtr mechanism and likely crashing.
+  // TODO(bartekn): Add a guard page at the end of direct map allocations.
 
   static constexpr size_t kGigaBytes = 1024 * 1024 * 1024;
 
@@ -108,59 +107,60 @@
   //
   // For example, [16GiB,8GiB] would work, but [8GiB,16GiB] wouldn't (the 2nd
   // pool is aligned on 8GiB but needs 16GiB), and [8GiB,8GiB,16GiB,1GiB] would.
-  static constexpr size_t kNonBRPPoolSize = 16 * kGigaBytes;
-  static constexpr size_t kBRPPoolSize = 16 * kGigaBytes;
+  static constexpr size_t kDirectMapPoolSize = 16 * kGigaBytes;
+  static constexpr size_t kNormalBucketPoolSize = 16 * kGigaBytes;
 
   static constexpr size_t kDesiredAddressSpaceSize =
-      kNonBRPPoolSize + kBRPPoolSize;
+      kDirectMapPoolSize + kNormalBucketPoolSize;
   static constexpr size_t kReservedAddressSpaceAlignment =
-      std::max(kNonBRPPoolSize, kBRPPoolSize);
+      std::max(kDirectMapPoolSize, kNormalBucketPoolSize);
 
-  static_assert(bits::IsPowerOfTwo(kNonBRPPoolSize) &&
-                    bits::IsPowerOfTwo(kBRPPoolSize),
+  static_assert(bits::IsPowerOfTwo(kDirectMapPoolSize) &&
+                    bits::IsPowerOfTwo(kNormalBucketPoolSize),
                 "Each pool size should be a power of two.");
   static_assert(bits::IsPowerOfTwo(kReservedAddressSpaceAlignment),
                 "kReservedAddressSpaceAlignment should be a power of two.");
-  static_assert(kReservedAddressSpaceAlignment >= kNonBRPPoolSize &&
-                    kReservedAddressSpaceAlignment >= kBRPPoolSize,
+  static_assert(kReservedAddressSpaceAlignment >= kDirectMapPoolSize &&
+                    kReservedAddressSpaceAlignment >= kNormalBucketPoolSize,
                 "kReservedAddressSpaceAlignment should be larger or equal to "
                 "each pool size.");
-  static_assert(kReservedAddressSpaceAlignment % kNonBRPPoolSize == 0 &&
-                    (kReservedAddressSpaceAlignment + kNonBRPPoolSize) %
-                            kBRPPoolSize ==
+  static_assert(kReservedAddressSpaceAlignment % kDirectMapPoolSize == 0 &&
+                    (kReservedAddressSpaceAlignment + kDirectMapPoolSize) %
+                            kNormalBucketPoolSize ==
                         0,
                 "Each pool should be aligned to its own size");
 
   // Masks used to easy determine belonging to a pool.
-  static constexpr uintptr_t kNonBRPPoolOffsetMask =
-      static_cast<uintptr_t>(kNonBRPPoolSize) - 1;
-  static constexpr uintptr_t kNonBRPPoolBaseMask = ~kNonBRPPoolOffsetMask;
-  static constexpr uintptr_t kBRPPoolOffsetMask =
-      static_cast<uintptr_t>(kBRPPoolSize) - 1;
-  static constexpr uintptr_t kBRPPoolBaseMask = ~kBRPPoolOffsetMask;
+  static constexpr uintptr_t kDirectMapPoolOffsetMask =
+      static_cast<uintptr_t>(kDirectMapPoolSize) - 1;
+  static constexpr uintptr_t kDirectMapPoolBaseMask = ~kDirectMapPoolOffsetMask;
+  static constexpr uintptr_t kNormalBucketPoolOffsetMask =
+      static_cast<uintptr_t>(kNormalBucketPoolSize) - 1;
+  static constexpr uintptr_t kNormalBucketPoolBaseMask =
+      ~kNormalBucketPoolOffsetMask;
 
   // See the comment describing the address layout above.
   static uintptr_t reserved_base_address_;
-  static uintptr_t non_brp_pool_base_address_;
-  static uintptr_t brp_pool_base_address_;
+  static uintptr_t direct_map_pool_base_address_;
+  static uintptr_t normal_bucket_pool_base_address_;
 
-  static internal::pool_handle non_brp_pool_;
-  static internal::pool_handle brp_pool_;
+  static internal::pool_handle direct_map_pool_;
+  static internal::pool_handle normal_bucket_pool_;
 };
 
-ALWAYS_INLINE internal::pool_handle GetNonBRPPool() {
+ALWAYS_INLINE internal::pool_handle GetDirectMapPool() {
   // This file is included from checked_ptr.h. This will result in a cycle if it
   // includes partition_alloc_features.h where IsPartitionAllocGigaCageEnabled
   // resides, because it includes Finch headers which may include checked_ptr.h.
   // TODO(bartekn): Uncomment once Finch is no longer used there.
   // PA_DCHECK(IsPartitionAllocGigaCageEnabled());
-  return PartitionAddressSpace::GetNonBRPPool();
+  return PartitionAddressSpace::GetDirectMapPool();
 }
 
-ALWAYS_INLINE internal::pool_handle GetBRPPool() {
+ALWAYS_INLINE internal::pool_handle GetNormalBucketPool() {
   // TODO(bartekn): Uncomment once Finch is no longer used there (see above).
   // PA_DCHECK(IsPartitionAllocGigaCageEnabled());
-  return PartitionAddressSpace::GetBRPPool();
+  return PartitionAddressSpace::GetNormalBucketPool();
 }
 
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
@@ -168,12 +168,12 @@
 }  // namespace internal
 
 #if defined(PA_HAS_64_BITS_POINTERS)
-ALWAYS_INLINE bool IsManagedByPartitionAllocNonBRPPool(const void* address) {
-  return internal::PartitionAddressSpace::IsInNonBRPPool(address);
+ALWAYS_INLINE bool IsManagedByPartitionAllocDirectMap(const void* address) {
+  return internal::PartitionAddressSpace::IsInDirectMapPool(address);
 }
 
-ALWAYS_INLINE bool IsManagedByPartitionAllocBRPPool(const void* address) {
-  return internal::PartitionAddressSpace::IsInBRPPool(address);
+ALWAYS_INLINE bool IsManagedByPartitionAllocNormalBuckets(const void* address) {
+  return internal::PartitionAddressSpace::IsInNormalBucketPool(address);
 }
 #endif
 
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 617dd8e..c04f3215 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2841,7 +2841,7 @@
 #endif
     size_t alignment = PageAllocationGranularity();
 #if defined(PA_HAS_64_BITS_POINTERS)
-    if (features::IsPartitionAllocGigaCageEnabled())
+    if (root.UsesGigaCage())
       alignment = kSuperPageSize;
 #endif
     size_t expected_direct_map_size =
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index 8591dda..e7c2b4a2 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -38,11 +38,10 @@
   PA_DCHECK(slot_size <= map_size);
 
   char* ptr = nullptr;
-  // Allocate from GigaCage, if enabled. In this case, use non-BRP pool, because
-  // BackupRefPtr isn't supported in direct maps.
+  // Allocate from GigaCage, if enabled.
   if (features::IsPartitionAllocGigaCageEnabled()) {
     ptr = internal::AddressPoolManager::GetInstance()->Reserve(
-        GetNonBRPPool(), nullptr, reserved_size);
+        GetDirectMapPool(), nullptr, reserved_size);
   } else {
     ptr = reinterpret_cast<char*>(
         AllocPages(nullptr, reserved_size, kSuperPageAlignment,
@@ -237,12 +236,13 @@
   // architectures.
   char* requested_address = root->next_super_page;
   char* super_page = nullptr;
-  // Allocate from GigaCage, if enabled. Route to the appropriate GigaCage pool
-  // based on BackupRefPtr support.
-  if (features::IsPartitionAllocGigaCageEnabled()) {
+  // Allocate from GigaCage, if enabled. However, the exception to this is when
+  // ref-count isn't allowed, as CheckedPtr assumes that everything inside
+  // GigaCage uses ref-count (specifically, inside the GigaCage's normal bucket
+  // pool).
+  if (root->UsesGigaCage()) {
     super_page = AddressPoolManager::GetInstance()->Reserve(
-        root->SupportsBRP() ? GetBRPPool() : GetNonBRPPool(), requested_address,
-        kSuperPageSize);
+        GetNormalBucketPool(), requested_address, kSuperPageSize);
   } else {
     super_page = reinterpret_cast<char*>(
         AllocPages(requested_address, kSuperPageSize, kSuperPageAlignment,
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc
index 553270d..83686fa 100644
--- a/base/allocator/partition_allocator/partition_page.cc
+++ b/base/allocator/partition_allocator/partition_page.cc
@@ -210,11 +210,10 @@
 void DeferredUnmap::Unmap() {
   PA_DCHECK(ptr && size > 0);
   if (features::IsPartitionAllocGigaCageEnabled()) {
-    // Currently this function is only called for direct-mapped allocations,
-    // which always belong to the non-BRP pool, provided that GigaCage is used.
-    PA_DCHECK(IsManagedByPartitionAllocNonBRPPool(ptr));
+    // Currently this function is only called for direct-mapped allocations.
+    PA_DCHECK(IsManagedByPartitionAllocDirectMap(ptr));
     internal::AddressPoolManager::GetInstance()->UnreserveAndDecommit(
-        internal::GetNonBRPPool(), ptr, size);
+        internal::GetDirectMapPool(), ptr, size);
   } else {
     FreePages(ptr, size);
   }
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index bf38e274..0c91354 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -312,10 +312,8 @@
   return super_page_base + kSuperPageSize - PartitionPageSize();
 }
 
-// CAUTION! Use only for normal buckets. Using on direct-mapped allocations may
-// lead to undefined behavior.
 ALWAYS_INLINE bool IsWithinSuperPagePayload(char* ptr, bool with_quarantine) {
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK.
+  PA_DCHECK(!IsManagedByPartitionAllocDirectMap(ptr));
   char* super_page_base = reinterpret_cast<char*>(
       reinterpret_cast<uintptr_t>(ptr) & kSuperPageBaseMask);
   char* payload_start = SuperPagePayloadBegin(super_page_base, with_quarantine);
@@ -327,15 +325,12 @@
 // an existing slot span. The function may return a pointer even inside a
 // decommitted or free slot span, it's the caller responsibility to check if
 // memory is actually allocated.
-//
-// CAUTION! Use only for normal buckets. Using on direct-mapped allocations may
-// lead to undefined behavior.
-//
-// Furthermore, |maybe_inner_ptr| must point to payload of a valid super page.
+// The precondition is that |maybe_inner_ptr| must point to payload of a valid
+// super page.
 template <bool thread_safe>
 ALWAYS_INLINE char* GetSlotStartInSuperPage(char* maybe_inner_ptr) {
 #if DCHECK_IS_ON()
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK.
+  PA_DCHECK(!IsManagedByPartitionAllocDirectMap(maybe_inner_ptr));
   char* super_page_ptr = reinterpret_cast<char*>(
       reinterpret_cast<uintptr_t>(maybe_inner_ptr) & kSuperPageBaseMask);
   auto* extent = reinterpret_cast<PartitionSuperPageExtentEntry<thread_safe>*>(
@@ -584,13 +579,11 @@
 
 enum class QuarantineBitmapType { kMutator, kScanner };
 
-// CAUTION! Use only for normal buckets. Using on direct-mapped allocations may
-// lead to undefined behavior.
 ALWAYS_INLINE QuarantineBitmap* QuarantineBitmapFromPointer(
     QuarantineBitmapType type,
     size_t pcscan_epoch,
     void* ptr) {
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK.
+  PA_DCHECK(!IsManagedByPartitionAllocDirectMap(ptr));
   auto* super_page_base = reinterpret_cast<char*>(
       reinterpret_cast<uintptr_t>(ptr) & kSuperPageBaseMask);
   auto* first_bitmap = SuperPageQuarantineBitmaps(super_page_base);
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index ad468d4..96f48bc2 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -4,10 +4,8 @@
 
 #include "base/allocator/partition_allocator/partition_root.h"
 
-#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/oom.h"
 #include "base/allocator/partition_allocator/page_allocator.h"
-#include "base/allocator/partition_allocator/partition_address_space.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_features.h"
 #include "base/allocator/partition_allocator/partition_bucket.h"
@@ -389,8 +387,10 @@
 }
 
 #if DCHECK_IS_ON()
-void DCheckIfManagedByPartitionAllocBRPPool(const void* ptr) {
-  PA_DCHECK(IsManagedByPartitionAllocBRPPool(ptr));
+void DCheckIfManagedByPartitionAllocNormalBuckets(const void* ptr) {
+  if (features::IsPartitionAllocGigaCageEnabled()) {
+    PA_DCHECK(IsManagedByPartitionAllocNormalBuckets(ptr));
+  }
 }
 #endif
 
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index dc5e278..60cbd86 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -73,11 +73,11 @@
 
 namespace internal {
 // Avoid including partition_address_space.h from this .h file, by moving the
-// call to IsManagedByPartitionAllocBRPPool into the .cc file.
+// call to IfManagedByPartitionAllocNormalBuckets into the .cc file.
 #if DCHECK_IS_ON()
-BASE_EXPORT void DCheckIfManagedByPartitionAllocBRPPool(const void* ptr);
+BASE_EXPORT void DCheckIfManagedByPartitionAllocNormalBuckets(const void* ptr);
 #else
-ALWAYS_INLINE void DCheckIfManagedByPartitionAllocBRPPool(const void*) {}
+ALWAYS_INLINE void DCheckIfManagedByPartitionAllocNormalBuckets(const void*) {}
 #endif
 }  // namespace internal
 
@@ -244,7 +244,7 @@
   ALWAYS_INLINE static bool IsValidSlotSpan(SlotSpan* slot_span);
   ALWAYS_INLINE static PartitionRoot* FromSlotSpan(SlotSpan* slot_span);
   ALWAYS_INLINE static PartitionRoot* FromSuperPage(char* super_page);
-  ALWAYS_INLINE static PartitionRoot* FromPointer(char* ptr);
+  ALWAYS_INLINE static PartitionRoot* FromPointerInNormalBucketPool(char* ptr);
 
   ALWAYS_INLINE void IncreaseCommittedPages(size_t len);
   ALWAYS_INLINE void DecreaseCommittedPages(size_t len);
@@ -332,12 +332,12 @@
     return total_size_of_committed_pages.load(std::memory_order_relaxed);
   }
 
-  bool SupportsBRP() const {
-    // Return the same value regardless of BUILDFLAG(USE_BACKUP_REF_PTR), so
-    // that the same choices are made in either configuration (though this may
-    // be not true in the BUILDFLAG(ENABLE_RUNTIME_BACKUP_REF_PTR_CONTROL)
-    // case).
-    return allow_ref_count;
+  bool UsesGigaCage() const {
+    return features::IsPartitionAllocGigaCageEnabled()
+#if BUILDFLAG(USE_BACKUP_REF_PTR)
+           && allow_ref_count
+#endif
+        ;
   }
 
   ALWAYS_INLINE bool IsQuarantineAllowed() const {
@@ -631,15 +631,12 @@
 }
 
 // Gets the SlotSpanMetadata object of the slot span that contains |ptr|. It's
-// used with intention to do obtain the slot size.
-//
-// CAUTION! Use only for normal buckets. Using on direct-mapped allocations may
-// lead to undefined behavior.
+// used with intention to do obtain the slot size. CAUTION! It works well for
+// normal buckets, but for direct-mapped allocations it'll only work if |ptr| is
+// in the first partition page of the allocation.
 template <bool thread_safe>
 ALWAYS_INLINE internal::SlotSpanMetadata<thread_safe>*
 PartitionAllocGetSlotSpanForSizeQuery(void* ptr) {
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK.
-
   // No need to lock here. Only |ptr| being freed by another thread could
   // cause trouble, and the caller is responsible for that not happening.
   auto* slot_span =
@@ -672,8 +669,7 @@
   // care of that detail.
   ptr = reinterpret_cast<char*>(ptr) - kPartitionPastAllocationAdjustment;
 
-  internal::DCheckIfManagedByPartitionAllocBRPPool(ptr);
-
+  internal::DCheckIfManagedByPartitionAllocNormalBuckets(ptr);
   auto* slot_span =
       internal::PartitionAllocGetSlotSpanForSizeQuery<internal::ThreadSafe>(
           ptr);
@@ -808,15 +804,17 @@
 
     // On Android, malloc() interception is more fragile than on other
     // platforms, as we use wrapped symbols. However, the GigaCage allows us to
-    // quickly tell that a pointer was allocated with PartitionAlloc.
+    // quickly tell that a pointer was allocated with PartitionAlloc. GigaCage
+    // is unfortunately not used for the aligned partition when BackupRefPtr is
+    // enabled, yielding the set of conditions below.
     //
     // This is a crash to detect imperfect symbol interception. However, we can
     // forward allocations we don't own to the system malloc() implementation in
     // these rare cases, assuming that some remain.
-#if defined(OS_ANDROID) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-  PA_DCHECK(features::IsPartitionAllocGigaCageEnabled());
-  PA_CHECK(IsManagedByPartitionAllocBRPPool(ptr) ||
-           IsManagedByPartitionAllocNonBRPPool(ptr));
+#if defined(OS_ANDROID) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    !BUILDFLAG(USE_BACKUP_REF_PTR)
+  PA_CHECK(IsManagedByPartitionAllocNormalBuckets(ptr) ||
+           IsManagedByPartitionAllocDirectMap(ptr));
 #endif
 
   // Call FromSlotInnerPtr instead of FromSlotStartPtr because the pointer
@@ -887,8 +885,7 @@
 
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
   if (allow_ref_count) {
-    if (LIKELY(!slot_span->bucket->is_direct_mapped() &&
-               features::IsPartitionAllocGigaCageEnabled())) {
+    if (LIKELY(!slot_span->bucket->is_direct_mapped())) {
       auto* ref_count = internal::PartitionRefCountPointer(slot_start);
       // If we are holding the last reference to the allocation, it can be freed
       // immediately. Otherwise, defer the operation and zap the memory to turn
@@ -993,12 +990,10 @@
   return root;
 }
 
-// CAUTION! Use only for normal buckets. Using on direct-mapped allocations may
-// lead to undefined behavior.
 template <bool thread_safe>
 ALWAYS_INLINE PartitionRoot<thread_safe>*
-PartitionRoot<thread_safe>::FromPointer(char* ptr) {
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK.
+PartitionRoot<thread_safe>::FromPointerInNormalBucketPool(char* ptr) {
+  PA_DCHECK(!IsManagedByPartitionAllocDirectMap(ptr));
   char* super_page = reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(ptr) &
                                              kSuperPageBaseMask);
   return FromSuperPage(super_page);
@@ -1296,8 +1291,7 @@
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
   bool is_direct_mapped = raw_size > kMaxBucketed;
   // LIKELY: Direct mapped allocations are large and rare.
-  if (allow_ref_count && LIKELY(!is_direct_mapped &&
-                                features::IsPartitionAllocGigaCageEnabled())) {
+  if (allow_ref_count && LIKELY(!is_direct_mapped)) {
     new (internal::PartitionRefCountPointer(slot_start))
         internal::PartitionRefCount();
   }
diff --git a/base/allocator/partition_allocator/pcscan.cc b/base/allocator/partition_allocator/pcscan.cc
index 88f8bdc..cf64e2e 100644
--- a/base/allocator/partition_allocator/pcscan.cc
+++ b/base/allocator/partition_allocator/pcscan.cc
@@ -213,8 +213,7 @@
   using LargeScanAreas =
       std::vector<LargeScanArea, MetadataAllocator<LargeScanArea>>;
 
-  // BRP pool is guaranteed to have only normal buckets, so everything there
-  // deals in super pages.
+  // Super pages only correspond to normal buckets.
   // TODO(bikineev): Consider flat containers since the number of elements is
   // relatively small. This requires making base containers allocator-aware.
   using SuperPages =
@@ -226,33 +225,35 @@
       for (uintptr_t super_page_base : super_pages) {
 #if DCHECK_IS_ON()
         PA_DCHECK(!(super_page_base % kSuperPageAlignment));
-        PA_DCHECK(IsManagedByPartitionAllocBRPPool(
+        PA_DCHECK(IsManagedByPartitionAllocNormalBuckets(
             reinterpret_cast<char*>(super_page_base)));
 #endif
-        bitset_.Set(BRPPoolOffset(super_page_base));
+        bitset_.Set(NormalBucketPoolOffset(super_page_base));
       }
     }
 
     ALWAYS_INLINE bool Test(uintptr_t maybe_ptr) const {
 #if DCHECK_IS_ON()
-      PA_DCHECK(
-          IsManagedByPartitionAllocBRPPool(reinterpret_cast<char*>(maybe_ptr)));
+      PA_DCHECK(IsManagedByPartitionAllocNormalBuckets(
+          reinterpret_cast<char*>(maybe_ptr)));
 #endif
-      return bitset_.Test(BRPPoolOffset(maybe_ptr));
+      return bitset_.Test(NormalBucketPoolOffset(maybe_ptr));
     }
 
    private:
     static constexpr size_t kBitmapSize =
-        AddressPoolManager::kBRPPoolMaxSize >> kSuperPageShift;
+        AddressPoolManager::kNormalBucketMaxSize >> kSuperPageShift;
 
-    ALWAYS_INLINE static constexpr size_t BRPPoolOffset(uintptr_t ptr) {
-      constexpr uintptr_t kBRPPoolMask =
+    ALWAYS_INLINE static constexpr size_t NormalBucketPoolOffset(
+        uintptr_t ptr) {
+      constexpr uintptr_t kNormalBucketPoolMask =
 #if defined(PA_HAS_64_BITS_POINTERS)
-          PartitionAddressSpace::BRPPoolBaseMask();
+          PartitionAddressSpace::NormalBucketPoolBaseMask();
 #else
           0;
 #endif
-      return static_cast<size_t>((ptr & ~kBRPPoolMask) >> kSuperPageShift);
+      return static_cast<size_t>((ptr & ~kNormalBucketPoolMask) >>
+                                 kSuperPageShift);
     }
 
     SimpleBitset<kBitmapSize> bitset_;
@@ -261,8 +262,8 @@
   struct BitmapLookupPolicy {
     ALWAYS_INLINE bool TestOnHeapPointer(uintptr_t maybe_ptr) const {
 #if DCHECK_IS_ON()
-      PA_DCHECK(
-          IsManagedByPartitionAllocBRPPool(reinterpret_cast<void*>(maybe_ptr)));
+      PA_DCHECK(IsManagedByPartitionAllocNormalBuckets(
+          reinterpret_cast<void*>(maybe_ptr)));
 #endif
       return task_.super_pages_bitmap_.Test(maybe_ptr);
     }
@@ -285,7 +286,8 @@
   // Lookup and marking functions. Return size of the object if marked or zero
   // otherwise.
   template <typename LookupPolicy>
-  ALWAYS_INLINE size_t TryMarkObjectInBRPPool(uintptr_t maybe_ptr) const;
+  ALWAYS_INLINE size_t
+  TryMarkObjectInNormalBucketPool(uintptr_t maybe_ptr) const;
 
   // Scans all registeres partitions and marks reachable quarantined objects.
   // Returns the size of marked objects.
@@ -314,9 +316,7 @@
 ALWAYS_INLINE QuarantineBitmap*
 PCScan<thread_safe>::PCScanTask::TryFindScannerBitmapForPointer(
     uintptr_t maybe_ptr) const {
-  // First, check if |maybe_ptr| points to a valid super page, containing normal
-  // buckets. If GigaCage is enabled, this will filter out addresses not in BRP
-  // pool (which are guaranteed to be normal buckets).
+  // First, check if |maybe_ptr| points to a valid super page.
   LookupPolicy lookup{*this};
   if (!lookup.TestOnHeapPointer(maybe_ptr))
     return nullptr;
@@ -332,39 +332,28 @@
 
 // Looks up and marks a potential dangling pointer. Returns the size of the slot
 // (which is then accounted as quarantined) or zero if no object is found.
-// For super pages in BRP pool, PCScan uses two quarantine bitmaps, the
+// For normal bucket super pages, PCScan uses two quarantine bitmaps, the
 // mutator and the scanner one. The former is used by mutators when objects are
 // freed, while the latter is used concurrently by the PCScan thread. The
 // bitmaps are swapped as soon as PCScan is triggered. Once a dangling pointer
 // (which points to an object in the scanner bitmap) is found,
-// TryMarkObjectInBRPPool() marks it again in the bitmap and clears
+// TryMarkObjectInNormalBucketPool() marks it again in the bitmap and clears
 // from the scanner bitmap. This way, when scanning is done, all uncleared
 // entries in the scanner bitmap correspond to unreachable objects.
 template <bool thread_safe>
 template <typename LookupPolicy>
-ALWAYS_INLINE size_t PCScan<thread_safe>::PCScanTask::TryMarkObjectInBRPPool(
+ALWAYS_INLINE size_t
+PCScan<thread_safe>::PCScanTask::TryMarkObjectInNormalBucketPool(
     uintptr_t maybe_ptr) const {
   using AccessType = QuarantineBitmap::AccessType;
-  // Check if |maybe_ptr| points somewhere to the protected part of the heap
-  // (BRP pool if GigaCage is enabled, normal buckets otherwise).
+  // Check if maybe_ptr points somewhere to the heap.
   auto* scanner_bitmap =
       TryFindScannerBitmapForPointer<LookupPolicy>(maybe_ptr);
   if (!scanner_bitmap)
     return 0;
 
-#if DCHECK_IS_ON()
-  // Out of necessity, skip the check if GigaCage isn't enabled, or it'd fail on
-  // RunUnvectorizedNoGigaCage path. This is ok because this function deals fine
-  // with all pointers to normal buckets, and TryFindScannerBitmapForPointer
-  // guarantees we wouldn't get here otherwise.
-  if (features::IsPartitionAllocGigaCageEnabled()) {
-    PA_DCHECK(
-        IsManagedByPartitionAllocBRPPool(reinterpret_cast<void*>(maybe_ptr)));
-  }
-  // TODO(bartekn): Add a "is in normal buckets" DCHECK in the |else| case.
-#endif  // DCHECK_IS_ON()
-
-  auto* root = Root::FromPointer(reinterpret_cast<char*>(maybe_ptr));
+  auto* root =
+      Root::FromPointerInNormalBucketPool(reinterpret_cast<char*>(maybe_ptr));
 
   // Check if pointer was in the quarantine bitmap.
   const uintptr_t base =
@@ -425,7 +414,7 @@
         pcscan_task_(pcscan_task)
 #if defined(PA_HAS_64_BITS_POINTERS)
         ,
-        brp_pool_base_(PartitionAddressSpace::BRPPoolBase())
+        normal_bucket_pool_base_(PartitionAddressSpace::NormalBucketPoolBase())
 #endif
   {
   }
@@ -465,9 +454,9 @@
   }
 
 #if defined(PA_HAS_64_BITS_POINTERS)
-  ALWAYS_INLINE bool IsInBRPPool(uintptr_t maybe_ptr) const {
-    return (maybe_ptr & PartitionAddressSpace::BRPPoolBaseMask()) ==
-           brp_pool_base_;
+  ALWAYS_INLINE bool IsInNormalBucketPool(uintptr_t maybe_ptr) const {
+    return (maybe_ptr & PartitionAddressSpace::NormalBucketPoolBaseMask()) ==
+           normal_bucket_pool_base_;
   }
 #endif
 
@@ -484,9 +473,10 @@
     // _mm_cmpeq_epi64), use packed doubles (not integers). Sticking to doubles
     // helps to avoid latency caused by "domain crossing penalties" (see bypass
     // delays in https://agner.org/optimize/microarchitecture.pdf).
-    const __m128d vbase = _mm_castsi128_pd(_mm_set1_epi64x(brp_pool_base_));
+    const __m128d vbase =
+        _mm_castsi128_pd(_mm_set1_epi64x(normal_bucket_pool_base_));
     const __m128d cage_mask = _mm_castsi128_pd(
-        _mm_set1_epi64x(PartitionAddressSpace::BRPPoolBaseMask()));
+        _mm_set1_epi64x(PartitionAddressSpace::NormalBucketPoolBaseMask()));
 
     size_t quarantine_size = 0;
     for (uintptr_t* payload = begin; payload < end; payload += kWordsInVector) {
@@ -501,7 +491,7 @@
       // avoid racing with the mutator.
       if (mask & 0b01) {
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm_cvtsi128_si64(_mm_castpd_si128(maybe_ptrs)));
       }
       if (mask & 0b10) {
@@ -512,7 +502,7 @@
         const __m128i shuffled =
             _mm_shuffle_epi32(_mm_castpd_si128(maybe_ptrs), kSecondWordMask);
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm_cvtsi128_si64(shuffled));
       }
     }
@@ -528,9 +518,9 @@
     // throughput. For example, according to the Intel docs, on Broadwell and
     // Haswell the CPI of vmovdqa (_mm256_load_si256) is twice smaller (0.25)
     // than that of vmovapd (_mm256_load_pd).
-    const __m256i vbase = _mm256_set1_epi64x(brp_pool_base_);
+    const __m256i vbase = _mm256_set1_epi64x(normal_bucket_pool_base_);
     const __m256i cage_mask =
-        _mm256_set1_epi64x(PartitionAddressSpace::BRPPoolBaseMask());
+        _mm256_set1_epi64x(PartitionAddressSpace::NormalBucketPoolBaseMask());
 
     size_t quarantine_size = 0;
     uintptr_t* payload = begin;
@@ -546,19 +536,19 @@
       // avoid racing with the mutator.
       if (mask & 0b0001)
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm256_extract_epi64(maybe_ptrs, 0));
       if (mask & 0b0010)
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm256_extract_epi64(maybe_ptrs, 1));
       if (mask & 0b0100)
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm256_extract_epi64(maybe_ptrs, 2));
       if (mask & 0b1000)
         quarantine_size +=
-            pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(
+            pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
                 _mm256_extract_epi64(maybe_ptrs, 3));
     }
 
@@ -574,17 +564,18 @@
     for (; begin < end; ++begin) {
       uintptr_t maybe_ptr = *begin;
 #if defined(PA_HAS_64_BITS_POINTERS)
-      // On 64bit architectures, call IsInBRPPool instead of
-      // IsManagedByPartitionAllocBRPPool to avoid redundant load of
-      // PartitionAddressSpace::brp_pool_base_address_.
-      if (LIKELY(!IsInBRPPool(maybe_ptr)))
+      // On 64bit architectures, call IsInNormalBucketPool instead of
+      // IsManagedByPartitionAllocNormalBuckets to avoid redundant load of
+      // PartitionAddressSpace::normal_bucket_pool_base_address_.
+      if (LIKELY(!IsInNormalBucketPool(maybe_ptr)))
 #else
-      if (LIKELY(!IsManagedByPartitionAllocBRPPool(
+      if (LIKELY(!IsManagedByPartitionAllocNormalBuckets(
               reinterpret_cast<void*>(maybe_ptr))))
 #endif
         continue;
       quarantine_size +=
-          pcscan_task_.TryMarkObjectInBRPPool<BitmapLookupPolicy>(maybe_ptr);
+          pcscan_task_.TryMarkObjectInNormalBucketPool<BitmapLookupPolicy>(
+              maybe_ptr);
     }
     return quarantine_size;
   }
@@ -598,7 +589,8 @@
       if (!maybe_ptr)
         continue;
       quarantine_size +=
-          pcscan_task_.TryMarkObjectInBRPPool<BinaryLookupPolicy>(maybe_ptr);
+          pcscan_task_.TryMarkObjectInNormalBucketPool<BinaryLookupPolicy>(
+              maybe_ptr);
     }
     return quarantine_size;
   }
@@ -607,8 +599,8 @@
   const PCScanTask& pcscan_task_;
 #if defined(PA_HAS_64_BITS_POINTERS)
   // Keep this a constant so that the compiler can remove redundant loads for
-  // the base of the BRP pool and hoist them out of the loops.
-  const uintptr_t brp_pool_base_;
+  // the base of the normal bucket pool and hoist them out of the loops.
+  const uintptr_t normal_bucket_pool_base_;
 #endif
 };
 
diff --git a/base/allocator/partition_allocator/pcscan_unittest.cc b/base/allocator/partition_allocator/pcscan_unittest.cc
index 6c4afa2..95349e6 100644
--- a/base/allocator/partition_allocator/pcscan_unittest.cc
+++ b/base/allocator/partition_allocator/pcscan_unittest.cc
@@ -23,7 +23,7 @@
     allocator_.init({PartitionOptions::Alignment::kRegular,
                      PartitionOptions::ThreadCache::kDisabled,
                      PartitionOptions::Quarantine::kAllowed,
-                     PartitionOptions::RefCount::kEnabled});
+                     PartitionOptions::RefCount::kDisabled});
     PCScan<ThreadSafe>::Instance().RegisterScannableRoot(allocator_.root());
   }
   ~PCScanTest() override {
@@ -170,8 +170,8 @@
 void TestDanglingReference(PCScanTest& test,
                            SourceList* source,
                            ValueList* value) {
-  auto* value_root =
-      ThreadSafePartitionRoot::FromPointer(reinterpret_cast<char*>(value));
+  auto* value_root = ThreadSafePartitionRoot::FromPointerInNormalBucketPool(
+      reinterpret_cast<char*>(value));
   {
     // Free |value| and leave the dangling reference in |source|.
     ValueList::Destroy(*value_root, value);
@@ -315,11 +315,11 @@
   ThreadSafePartitionRoot source_root({PartitionOptions::Alignment::kRegular,
                                        PartitionOptions::ThreadCache::kDisabled,
                                        PartitionOptions::Quarantine::kAllowed,
-                                       PartitionOptions::RefCount::kEnabled});
+                                       PartitionOptions::RefCount::kDisabled});
   ThreadSafePartitionRoot value_root({PartitionOptions::Alignment::kRegular,
                                       PartitionOptions::ThreadCache::kDisabled,
                                       PartitionOptions::Quarantine::kAllowed,
-                                      PartitionOptions::RefCount::kEnabled});
+                                      PartitionOptions::RefCount::kDisabled});
 
   PCScan<ThreadSafe>::Instance().RegisterScannableRoot(&source_root);
   PCScan<ThreadSafe>::Instance().RegisterScannableRoot(&value_root);
@@ -338,11 +338,11 @@
   ThreadSafePartitionRoot source_root({PartitionOptions::Alignment::kRegular,
                                        PartitionOptions::ThreadCache::kDisabled,
                                        PartitionOptions::Quarantine::kAllowed,
-                                       PartitionOptions::RefCount::kEnabled});
+                                       PartitionOptions::RefCount::kDisabled});
   ThreadSafePartitionRoot value_root({PartitionOptions::Alignment::kRegular,
                                       PartitionOptions::ThreadCache::kDisabled,
                                       PartitionOptions::Quarantine::kAllowed,
-                                      PartitionOptions::RefCount::kEnabled});
+                                      PartitionOptions::RefCount::kDisabled});
 
   PCScan<ThreadSafe>::Instance().RegisterScannableRoot(&source_root);
   PCScan<ThreadSafe>::Instance().RegisterNonScannableRoot(&value_root);
@@ -362,12 +362,12 @@
       {PartitionOptions::Alignment::kRegular,
        PartitionOptions::ThreadCache::kDisabled,
        base::PartitionOptions::Quarantine::kAllowed,
-       PartitionOptions::RefCount::kEnabled});
+       PartitionOptions::RefCount::kDisabled});
   ThreadSafePartitionRoot value_root(
       {PartitionOptions::Alignment::kRegular,
        PartitionOptions::ThreadCache::kDisabled,
        base::PartitionOptions::Quarantine::kAllowed,
-       PartitionOptions::RefCount::kEnabled});
+       PartitionOptions::RefCount::kDisabled});
 
   PCScan<ThreadSafe>::Instance().RegisterNonScannableRoot(&source_root);
   PCScan<ThreadSafe>::Instance().RegisterScannableRoot(&value_root);
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 18bcb29..4df3649 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -1508,11 +1508,17 @@
   return os.sep.join(script_components[base_index:])
 
 
-def _RemoveExistingHeaders(path):
-  if os.path.exists(path) and os.path.isdir(path):
-    for root, _, files in os.walk(path):
-      for f in files:
-        file_path = os.path.join(root, f)
+def _RemoveStaleHeaders(path, output_files):
+  if not os.path.isdir(path):
+    return
+  # Do not remove output files so that timestamps on declared outputs are not
+  # modified unless their contents are changed (avoids reverse deps needing to
+  # be rebuilt).
+  preserve = set(output_files)
+  for root, _, files in os.walk(path):
+    for f in files:
+      file_path = os.path.join(root, f)
+      if file_path not in preserve:
         if os.path.isfile(file_path) and os.path.splitext(file_path)[1] == '.h':
           os.remove(file_path)
 
@@ -1604,7 +1610,7 @@
     # Remove existing headers so that moving .java source files but not updating
     # the corresponding C++ include will be a compile failure (otherwise
     # incremental builds will usually not catch this).
-    _RemoveExistingHeaders(output_dir)
+    _RemoveStaleHeaders(output_dir, output_files)
   else:
     output_files = [None] * len(input_files)
   temp_dir = tempfile.mkdtemp()
diff --git a/base/atomicops.h b/base/atomicops.h
index 3784289e..d3265dd 100644
--- a/base/atomicops.h
+++ b/base/atomicops.h
@@ -104,7 +104,6 @@
 
 Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
 Atomic32 Acquire_Load(volatile const Atomic32* ptr);
-Atomic32 Release_Load(volatile const Atomic32* ptr);
 
 // 64-bit atomic operations (only available on 64-bit processors).
 #ifdef ARCH_CPU_64_BITS
@@ -125,7 +124,6 @@
 void Release_Store(volatile Atomic64* ptr, Atomic64 value);
 Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
 Atomic64 Acquire_Load(volatile const Atomic64* ptr);
-Atomic64 Release_Load(volatile const Atomic64* ptr);
 #endif  // ARCH_CPU_64_BITS
 
 }  // namespace subtle
diff --git a/base/atomicops_internals_atomicword_compat.h b/base/atomicops_internals_atomicword_compat.h
index d066ef4..69e1514 100644
--- a/base/atomicops_internals_atomicword_compat.h
+++ b/base/atomicops_internals_atomicword_compat.h
@@ -86,11 +86,6 @@
       reinterpret_cast<volatile const Atomic32*>(ptr));
 }
 
-inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
-  return base::subtle::Release_Load(
-      reinterpret_cast<volatile const Atomic32*>(ptr));
-}
-
 }  // namespace subtle
 }  // namespace base
 
diff --git a/base/atomicops_internals_portable.h b/base/atomicops_internals_portable.h
index 3fd4567..2c4f5af 100644
--- a/base/atomicops_internals_portable.h
+++ b/base/atomicops_internals_portable.h
@@ -19,11 +19,6 @@
 //  * Compare exchange's failure ordering is always the same as the success one
 //    (except for release, which fails as relaxed): using a weaker ordering is
 //    only valid under certain uses of compare exchange.
-//  * Acquire store doesn't exist in the C11 memory model, it is instead
-//    implemented as a relaxed store followed by a sequentially consistent
-//    fence.
-//  * Release load doesn't exist in the C11 memory model, it is instead
-//    implemented as sequentially consistent fence followed by a relaxed load.
 //  * Atomic increment is expected to return the post-incremented value, whereas
 //    C11 fetch add returns the previous value. The implementation therefore
 //    needs to increment twice (which the compiler should be able to detect and
@@ -119,11 +114,6 @@
   return ((AtomicLocation32)ptr)->load(std::memory_order_acquire);
 }
 
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
-  std::atomic_thread_fence(std::memory_order_seq_cst);
-  return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
-}
-
 #if defined(ARCH_CPU_64_BITS)
 
 typedef volatile std::atomic<Atomic64>* AtomicLocation64;
@@ -197,11 +187,6 @@
   return ((AtomicLocation64)ptr)->load(std::memory_order_acquire);
 }
 
-inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
-  std::atomic_thread_fence(std::memory_order_seq_cst);
-  return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
-}
-
 #endif  // defined(ARCH_CPU_64_BITS)
 }  // namespace subtle
 }  // namespace base
diff --git a/base/atomicops_internals_x86_msvc.h b/base/atomicops_internals_x86_msvc.h
index 102d071..32d0792b 100644
--- a/base/atomicops_internals_x86_msvc.h
+++ b/base/atomicops_internals_x86_msvc.h
@@ -79,11 +79,6 @@
   return value;
 }
 
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
-  std::atomic_thread_fence(std::memory_order_seq_cst);
-  return *ptr;
-}
-
 #if defined(_WIN64)
 
 // 64-bit low-level operations on 64-bit platform.
@@ -143,11 +138,6 @@
   return value;
 }
 
-inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
-  std::atomic_thread_fence(std::memory_order_seq_cst);
-  return *ptr;
-}
-
 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
                                        Atomic64 old_value,
                                        Atomic64 new_value) {
diff --git a/base/atomicops_unittest.cc b/base/atomicops_unittest.cc
index 8305ee6..375e4366 100644
--- a/base/atomicops_unittest.cc
+++ b/base/atomicops_unittest.cc
@@ -205,11 +205,6 @@
   EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value));
   value = kVal2;
   EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value));
-
-  value = kVal1;
-  EXPECT_EQ(kVal1, base::subtle::Release_Load(&value));
-  value = kVal2;
-  EXPECT_EQ(kVal2, base::subtle::Release_Load(&value));
 }
 
 TEST(AtomicOpsTest, Inc) {
diff --git a/base/fuchsia/intl_profile_watcher_unittest.cc b/base/fuchsia/intl_profile_watcher_unittest.cc
index 889d41a3..27d403e 100644
--- a/base/fuchsia/intl_profile_watcher_unittest.cc
+++ b/base/fuchsia/intl_profile_watcher_unittest.cc
@@ -119,19 +119,17 @@
       delete;
   ~FakePropertyProviderAsync() = default;
 
-  void Close() {
-    property_provider_.Post(FROM_HERE, &FakePropertyProvider::Close);
-  }
+  void Close() { property_provider_.AsyncCall(&FakePropertyProvider::Close); }
   void SetTimeZones(const std::vector<std::string>& zone_ids) {
-    property_provider_.Post(FROM_HERE, &FakePropertyProvider::SetTimeZones,
-                            zone_ids);
+    property_provider_.AsyncCall(&FakePropertyProvider::SetTimeZones)
+        .WithArgs(zone_ids);
   }
   void SetLocales(const std::vector<std::string>& locale_ids) {
-    property_provider_.Post(FROM_HERE, &FakePropertyProvider::SetLocales,
-                            locale_ids);
+    property_provider_.AsyncCall(&FakePropertyProvider::SetLocales)
+        .WithArgs(locale_ids);
   }
   void NotifyChange() {
-    property_provider_.Post(FROM_HERE, &FakePropertyProvider::NotifyChange);
+    property_provider_.AsyncCall(&FakePropertyProvider::NotifyChange);
   }
 
  private:
diff --git a/base/memory/checked_ptr.cc b/base/memory/checked_ptr.cc
index 19d28cac..ef4451a 100644
--- a/base/memory/checked_ptr.cc
+++ b/base/memory/checked_ptr.cc
@@ -4,7 +4,6 @@
 
 #include "base/memory/checked_ptr.h"
 
-#include "base/check.h"
 #include "base/partition_alloc_buildflags.h"
 
 // USE_BACKUP_REF_PTR implies USE_PARTITION_ALLOC, needed for code under
@@ -18,23 +17,17 @@
 namespace internal {
 
 void BackupRefPtrImpl::AcquireInternal(void* ptr) {
-  DCHECK(features::IsPartitionAllocGigaCageEnabled() &&
-         IsManagedByPartitionAllocBRPPool(ptr));
   void* slot_start = PartitionAllocGetSlotStart(ptr);
   PartitionRefCountPointer(slot_start)->Acquire();
 }
 
 void BackupRefPtrImpl::ReleaseInternal(void* ptr) {
-  DCHECK(features::IsPartitionAllocGigaCageEnabled() &&
-         IsManagedByPartitionAllocBRPPool(ptr));
   void* slot_start = PartitionAllocGetSlotStart(ptr);
   if (PartitionRefCountPointer(slot_start)->Release())
     PartitionAllocFreeForRefCounting(slot_start);
 }
 
 bool BackupRefPtrImpl::IsPointeeAlive(void* ptr) {
-  DCHECK(features::IsPartitionAllocGigaCageEnabled() &&
-         IsManagedByPartitionAllocBRPPool(ptr));
   void* slot_start = PartitionAllocGetSlotStart(ptr);
   return PartitionRefCountPointer(slot_start)->IsAlive();
 }
diff --git a/base/memory/checked_ptr.h b/base/memory/checked_ptr.h
index 112c32fd..db52b27 100644
--- a/base/memory/checked_ptr.h
+++ b/base/memory/checked_ptr.h
@@ -107,27 +107,28 @@
 
   static ALWAYS_INLINE bool IsSupportedAndNotNull(void* ptr) {
     // There is a problem on 32-bit systems, where the fake "GigaCage" has many
-    // BRP pool regions spread throughout the address space. A pointer
-    // immediately past an allocation may accidentally fall into the BRP pool,
+    // normal bucket pool regions spread throughout the address space. A pointer
+    // immediately past an allocation may fall into the normal bucket pool,
     // hence check if |ptr-1| belongs to that pool. However, checking only
     // |ptr-1| causes a problem with pointers to the beginning of an
     // out-of-the-pool allocation that happen to be where the pool ends, so
     // checking for |ptr| is also necessary.
     //
-    // Note, if |ptr| is in the BRP pool, |ptr-1| will not fall out of it,
-    // thanks to the leading guard pages (i.e. |ptr| will never point to the
+    // Note, if |ptr| is in the normal bucket pool, |ptr-1| will not fall out of
+    // it, thanks to the leading guard pages (i.e. |ptr| will never point to the
     // beginning of GigaCage).
     //
-    // 64-bit systems don't have this problem, because there is only one BRP
-    // pool region, positioned *after* the non-BRP pool.
-    bool is_in_brp_pool = true;
+    // 64-bit systems don't have this problem, because there is only one normal
+    // bucket pool region, positioned after the direct map pool.
+    bool is_in_normal_buckets = true;
 #if !(defined(ARCH_CPU_64_BITS) && !defined(OS_NACL))
     auto* adjusted_ptr = reinterpret_cast<char*>(ptr) - 1;
-    is_in_brp_pool &= IsManagedByPartitionAllocBRPPool(adjusted_ptr);
+    is_in_normal_buckets &=
+        IsManagedByPartitionAllocNormalBuckets(adjusted_ptr);
 #endif
     // This covers the nullptr case, as address 0 is never in GigaCage.
-    is_in_brp_pool &= IsManagedByPartitionAllocBRPPool(ptr);
-    return is_in_brp_pool;
+    is_in_normal_buckets &= IsManagedByPartitionAllocNormalBuckets(ptr);
+    return is_in_normal_buckets;
   }
 
   // Wraps a pointer, and returns its uintptr_t representation.
@@ -213,7 +214,7 @@
   // We've evaluated several strategies (inline nothing, various parts, or
   // everything in |Wrap()| and |Release()|) using the Speedometer2 benchmark
   // to measure performance. The best results were obtained when only the
-  // lightweight |IsManagedByPartitionAllocBRPPool()| check was inlined.
+  // lightweight |IsManagedByPartitionAllocNormalBuckets()| check was inlined.
   // Therefore, we've extracted the rest into the functions below and marked
   // them as NOINLINE to prevent unintended LTO effects.
   static BASE_EXPORT NOINLINE void AcquireInternal(void* ptr);
diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc
index ef08004c..d9bc848 100644
--- a/base/message_loop/message_pump_win.cc
+++ b/base/message_loop/message_pump_win.cc
@@ -781,9 +781,15 @@
     // Save this item for later
     completed_io_.push_back(item);
   } else {
-    TRACE_EVENT2("base,toplevel", "IOHandler::OnIOCompleted", "dest_file",
-                 item.handler->io_handler_location().file_name(), "dest_func",
-                 item.handler->io_handler_location().function_name());
+    TRACE_EVENT(
+        "base,toplevel", "IOHandler::OnIOCompleted",
+        [&](perfetto::EventContext ctx) {
+          ctx.event()->set_chrome_message_pump()->set_io_handler_location_iid(
+              base::trace_event::InternedSourceLocation::Get(
+                  &ctx, base::trace_event::TraceSourceLocation(
+                            item.handler->io_handler_location())));
+        });
+
     item.handler->OnIOCompleted(item.context, item.bytes_transfered,
                                 item.error);
   }
diff --git a/base/threading/sequence_bound.h b/base/threading/sequence_bound.h
index a30e254..c1690fa6 100644
--- a/base/threading/sequence_bound.h
+++ b/base/threading/sequence_bound.h
@@ -200,18 +200,6 @@
     return AsyncCallBuilder<R (T::*)(Args...) const>(this, &location, method);
   }
 
-  // Post a call to `method` to `impl_task_runner_`.
-  // TODO(dcheng): Deprecate this in favor of `AsyncCall()`.
-  template <typename... MethodArgs, typename... Args>
-  void Post(const base::Location& from_here,
-            void (T::*method)(MethodArgs...),
-            Args&&... args) const {
-    DCHECK(t_);
-    impl_task_runner_->PostTask(from_here,
-                                base::BindOnce(method, base::Unretained(t_),
-                                               std::forward<Args>(args)...));
-  }
-
   // Posts `task` to `impl_task_runner_`, passing it a reference to the wrapped
   // object. This allows arbitrary logic to be safely executed on the object's
   // task runner. The object is guaranteed to remain alive for the duration of
diff --git a/base/threading/sequence_bound_unittest.cc b/base/threading/sequence_bound_unittest.cc
index f907458..8fd4f6fdf 100644
--- a/base/threading/sequence_bound_unittest.cc
+++ b/base/threading/sequence_bound_unittest.cc
@@ -107,12 +107,12 @@
 };
 
 #if defined(OS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ConstructThenPostThenReset FLAKY_ConstructThenPostThenReset
+#define MAYBE_ConstructAsyncCallReset FLAKY_ConstructAsyncCallReset
 #else
-#define MAYBE_ConstructThenPostThenReset ConstructThenPostThenReset
+#define MAYBE_ConstructAsyncCallReset ConstructAsyncCallReset
 #endif
 // https://crbug.com/899779 tracks test flakiness on iOS.
-TEST_F(SequenceBoundTest, MAYBE_ConstructThenPostThenReset) {
+TEST_F(SequenceBoundTest, MAYBE_ConstructAsyncCallReset) {
   auto derived = SequenceBound<Derived>(task_runner_, &value_);
   EXPECT_FALSE(derived.is_null());
   EXPECT_TRUE(derived);
@@ -123,7 +123,7 @@
   EXPECT_EQ(value_, kDerivedCtorValue);
 
   // Post now that the object has been constructed.
-  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+  derived.AsyncCall(&Derived::SetValue).WithArgs(kDifferentValue);
   EXPECT_EQ(value_, kDerivedCtorValue);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(value_, kDifferentValue);
@@ -142,7 +142,7 @@
   // Construct an object and post a message to it, before construction has been
   // run on |task_runner_|.
   auto derived = SequenceBound<Derived>(task_runner_, &value_);
-  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+  derived.AsyncCall(&Derived::SetValue).WithArgs(kDifferentValue);
   EXPECT_EQ(value_, kInitialValue);
   // Both construction and SetValue should run.
   base::RunLoop().RunUntilIdle();
@@ -294,7 +294,7 @@
 
   // Cast to Other, the left base.
   SequenceBound<Other> other(std::move(mderived));
-  other.Post(FROM_HERE, &Other::SetValue, kDifferentValue);
+  other.AsyncCall(&Other::SetValue).WithArgs(kDifferentValue);
 
   base::RunLoop().RunUntilIdle();
 
@@ -313,7 +313,7 @@
       SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
 
   SequenceBound<Derived> derived(std::move(mderived));
-  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+  derived.AsyncCall(&Derived::SetValue).WithArgs(kDifferentValue);
 
   base::RunLoop().RunUntilIdle();
 
@@ -349,7 +349,7 @@
   Value* value_ptr = &value;
   SequenceBound<Derived> derived(task_runner_, value_ptr);
   {
-    derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+    derived.AsyncCall(&Derived::SetValue).WithArgs(kDifferentValue);
     base::RunLoop run_loop;
     task_runner_->PostTask(FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
@@ -390,8 +390,8 @@
   // Verify that the callback passed to ResetWithCallbackAfterDestruction always
   // does happen *after* destruction.
   bool destroyed = false;
-  value.Post(FROM_HERE, &BoxedValue::set_destruction_callback,
-             base::BindLambdaForTesting([&] { destroyed = true; }));
+  value.AsyncCall(&BoxedValue::set_destruction_callback)
+      .WithArgs(base::BindLambdaForTesting([&] { destroyed = true; }));
 
   base::RunLoop loop;
   value.ResetWithCallbackAfterDestruction(base::BindLambdaForTesting([&] {
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index cc4c9bd4..ce16488c 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -98,11 +98,15 @@
 // that's okay.
 
 class BrowserProcessImpl;
+class ChromeJsErrorReportProcessor;
 class ChromeNSSCryptoModuleDelegate;
 class HistogramSynchronizer;
 class KeyStorageLinux;
 class NativeBackendKWallet;
 class NativeDesktopMediaList;
+class Profile;
+
+Profile* GetLastProfileMac();
 
 namespace android_webview {
 class AwFormDatabaseService;
@@ -419,6 +423,7 @@
   friend class weblayer::WebLayerPathProvider;
 
   friend bool PathProviderWin(int, FilePath*);
+  friend Profile* ::GetLastProfileMac();  // crbug.com/1176734
 
   ScopedAllowBlocking(const Location& from_here = Location::Current());
   ~ScopedAllowBlocking();
@@ -467,6 +472,7 @@
   friend class blink::scheduler::WorkerThread;
   friend class chrome_cleaner::ResetShortcutsComponent;
   friend class chrome_cleaner::SystemReportComponent;
+  friend class ::ChromeJsErrorReportProcessor;
   friend class content::BrowserMainLoop;
   friend class content::BrowserProcessSubThread;
   friend class content::ServiceWorkerContextClient;
diff --git a/base/trace_event/base_tracing.h b/base/trace_event/base_tracing.h
index ae3c60e7..299d23f 100644
--- a/base/trace_event/base_tracing.h
+++ b/base/trace_event/base_tracing.h
@@ -17,6 +17,7 @@
 // TODO(crbug/1006541): Switch to perfetto for trace event implementation.
 #include "base/trace_event/blame_context.h"
 #include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/interned_args_helper.h"
 #include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/task_execution_macros.h"
diff --git a/base/trace_event/interned_args_helper.h b/base/trace_event/interned_args_helper.h
index f2a1a53..0a18438 100644
--- a/base/trace_event/interned_args_helper.h
+++ b/base/trace_event/interned_args_helper.h
@@ -8,6 +8,7 @@
 #include "base/base_export.h"
 #include "base/containers/span.h"
 #include "base/hash/hash.h"
+#include "base/location.h"
 #include "third_party/perfetto/include/perfetto/tracing/track_event_interned_data_index.h"
 #include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 
@@ -30,6 +31,13 @@
       : function_name(function_name),
         file_name(file_name),
         line_number(line_number) {}
+  // Construct a new source location from an existing base::Location, the only
+  // attributes that are read are |function_name|, |file_name| and
+  // |line_number|.
+  explicit TraceSourceLocation(const base::Location& location)
+      : function_name(location.function_name()),
+        file_name(location.file_name()),
+        line_number(location.line_number()) {}
 
   bool operator==(const TraceSourceLocation& other) const {
     return file_name == other.file_name &&
diff --git a/base/trace_event/task_execution_macros.h b/base/trace_event/task_execution_macros.h
index 110776b9..104d4798 100644
--- a/base/trace_event/task_execution_macros.h
+++ b/base/trace_event/task_execution_macros.h
@@ -18,10 +18,8 @@
   TRACE_EVENT("toplevel", run_function, [&](perfetto::EventContext ctx) { \
     ctx.event()->set_task_execution()->set_posted_from_iid(               \
         base::trace_event::InternedSourceLocation::Get(                   \
-            &ctx, base::trace_event::TraceSourceLocation(                 \
-                      (task).posted_from.function_name(),                 \
-                      (task).posted_from.file_name(),                     \
-                      (task).posted_from.line_number())));                \
+            &ctx,                                                         \
+            base::trace_event::TraceSourceLocation((task).posted_from))); \
   });                                                                     \
   TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION INTERNAL_TRACE_EVENT_UID( \
       task_event)((task).posted_from.file_name());                        \
diff --git a/base/trace_event/typed_macros_unittest.cc b/base/trace_event/typed_macros_unittest.cc
index 3c455c3..3140ffdfb 100644
--- a/base/trace_event/typed_macros_unittest.cc
+++ b/base/trace_event/typed_macros_unittest.cc
@@ -193,6 +193,15 @@
     size_t iid3 = InternedSourceLocation::Get(&ctx, location2);
     EXPECT_NE(0u, iid3);
     EXPECT_NE(iid, iid3);
+
+    // Test getting an interned ID directly from a base::Location object, the
+    // only attributes that are compared are function_name, file_name and
+    // line_number.
+    base::Location base_location =
+        base::Location::Current("TestFunction", "test.cc", 123);
+    TraceSourceLocation location3(base_location);
+    size_t iid4 = InternedSourceLocation::Get(&ctx, location3);
+    EXPECT_EQ(iid, iid4);
   });
 
   auto serialized_data =
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 0973e06e..62c2c2d 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -489,6 +489,13 @@
        third_party/android_sdk/public/build-tools/*/dexdump -d \
 out/Release/apks/YourApk.apk > dex.txt
 """ + stderr
+
+        if 'FragmentActivity' in stderr:
+          stderr += """
+You may need to update build configs to run FragmentActivityReplacer for
+additional targets. See
+https://chromium.googlesource.com/chromium/src.git/+/master/docs/ui/android/bytecode_rewriting.md.
+"""
       elif had_unfiltered_items:
         # Left only with empty headings. All indented items filtered out.
         stderr = ''
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 8bd5a00..be88524 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -1205,6 +1205,9 @@
     # We've tried to disable retries in the past with mixed results.
     # See crbug.com/619055 for historical context and crbug.com/797002
     # for ongoing efforts.
+    if 'Batch' in test['annotations'] and test['annotations']['Batch'][
+        'value'] == 'UnitTests':
+      return False
     del test, result
     return True
 
diff --git a/build/android/test_wrapper/logdog_wrapper.py b/build/android/test_wrapper/logdog_wrapper.py
index fda9f14..fdb3fd3 100755
--- a/build/android/test_wrapper/logdog_wrapper.py
+++ b/build/android/test_wrapper/logdog_wrapper.py
@@ -34,11 +34,17 @@
 def CommandParser():
   # Parses the command line arguments being passed in
   parser = argparse.ArgumentParser()
-  parser.add_argument(
+  wrapped = parser.add_mutually_exclusive_group()
+  wrapped.add_argument(
       '--target',
-      help='The test target to be run. If not set, any extra '
-      'args passed to this script are assumed to be the '
-      'full test command to run.')
+      help='The test target to be run. If neither target nor script are set,'
+      ' any extra args passed to this script are assumed to be the'
+      ' full test command to run.')
+  wrapped.add_argument(
+      '--script',
+      help='The script target to be run. If neither target nor script are set,'
+      ' any extra args passed to this script are assumed to be the'
+      ' full test command to run.')
   parser.add_argument('--logdog-bin-cmd', required=True,
                       help='The logdog bin cmd.')
   return parser
@@ -73,6 +79,9 @@
   if args.target:
     test_cmd = [os.path.join('bin', 'run_%s' % args.target), '-v']
     test_cmd += extra_cmd_args
+  elif args.script:
+    test_cmd = [args.script]
+    test_cmd += extra_cmd_args
   else:
     test_cmd = extra_cmd_args
 
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index b6fb649..be0a389be 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -740,11 +740,11 @@
 
   test_env = setup_env()
   if args.deploy_chrome:
+    # Mounting ash-chrome gives it enough disk space to not need stripping.
+    cros_run_test_cmd.extend(['--deploy-lacros'] if args.deploy_lacros else
+                             ['--deploy', '--mount', '--nostrip'])
+
     cros_run_test_cmd += [
-        '--deploy',
-        # Mounting ash-chrome gives it enough disk space to not need stripping.
-        '--mount',
-        '--nostrip',
         '--build-dir',
         os.path.abspath(args.path_to_outdir),
     ]
@@ -861,6 +861,10 @@
       action='store_true',
       help='Will deploy a locally built ash-chrome binary to the device before '
       'running the host-cmd.')
+  host_cmd_parser.add_argument(
+      '--deploy-lacros',
+      action='store_true',
+      help='Deploy a lacros-chrome instead of ash-chrome.')
 
   # GTest args.
   # TODO(bpastene): Rename 'vm-test' arg to 'gtest'.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index ca50d6a2..1d99d52 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -3937,7 +3937,7 @@
     }
 
     if (_build_host_jar || _build_device_jar) {
-      _process_prebuilt_target_name = "${target_name}_process"
+      _process_prebuilt_target_name = "${target_name}__process"
       process_java_prebuilt(_process_prebuilt_target_name) {
         forward_variables_from(invoker,
                                [
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn
index 8ac3166c..21739874 100644
--- a/build/config/clang/BUILD.gn
+++ b/build/config/clang/BUILD.gn
@@ -46,9 +46,10 @@
     data = [ "$clang_base_path/bin/llvm-symbolizer.exe" ]
   } else {
     data = [ "$clang_base_path/bin/llvm-symbolizer" ]
-  }
-  if (is_linux) {
-    # llvm-symbolizer uses libstdc++ from the clang package.
-    data += [ "$clang_base_path/lib/libstdc++.so.6" ]
+
+    if (!is_apple) {
+      # llvm-symbolizer uses libstdc++ from the clang package.
+      data += [ "$clang_base_path/lib/libstdc++.so.6" ]
+    }
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f99a1d4..a88e36b 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210210.0.1
+0.20210210.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index f99a1d4..a88e36b 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210210.0.1
+0.20210210.1.1
diff --git a/build/skia_gold_common/skia_gold_session.py b/build/skia_gold_common/skia_gold_session.py
index 7966e40..cb737ec5 100644
--- a/build/skia_gold_common/skia_gold_session.py
+++ b/build/skia_gold_common/skia_gold_session.py
@@ -44,7 +44,13 @@
       self.local_diff_closest_image = None
       self.local_diff_diff_image = None
 
-  def __init__(self, working_dir, gold_properties, keys_file, corpus, instance):
+  def __init__(self,
+               working_dir,
+               gold_properties,
+               keys_file,
+               corpus,
+               instance,
+               bucket=None):
     """Abstract class to handle all aspects of image comparison via Skia Gold.
 
     A single SkiaGoldSession is valid for a single instance/corpus/keys_file
@@ -59,11 +65,14 @@
           configuration the images will be produced on.
       corpus: The corpus that images that will be compared belong to.
       instance: The name of the Skia Gold instance to interact with.
+      bucket: Overrides the formulaic Google Storage bucket name generated by
+          goldctl
     """
     self._working_dir = working_dir
     self._gold_properties = gold_properties
     self._corpus = corpus
     self._instance = instance
+    self._bucket = bucket
     self._triage_link_file = tempfile.NamedTemporaryFile(suffix='.txt',
                                                          dir=working_dir,
                                                          delete=False).name
@@ -214,6 +223,8 @@
         '--commit',
         self._gold_properties.git_revision,
     ]
+    if self._bucket:
+      init_cmd.extend(['--bucket', self._bucket])
     if self._gold_properties.IsTryjobRun():
       init_cmd.extend([
           '--issue',
diff --git a/build/skia_gold_common/skia_gold_session_manager.py b/build/skia_gold_common/skia_gold_session_manager.py
index b1732f4..d4166e1 100644
--- a/build/skia_gold_common/skia_gold_session_manager.py
+++ b/build/skia_gold_common/skia_gold_session_manager.py
@@ -24,7 +24,11 @@
     self._gold_properties = gold_properties
     self._sessions = {}
 
-  def GetSkiaGoldSession(self, keys_input, corpus=None, instance=None):
+  def GetSkiaGoldSession(self,
+                         keys_input,
+                         corpus=None,
+                         instance=None,
+                         bucket=None):
     """Gets a SkiaGoldSession for the given arguments.
 
     Lazily creates one if necessary.
@@ -36,8 +40,10 @@
           file containing JSON to read.
       corpus: A string containing the corpus the session is for. If None, the
           corpus will be determined using available information.
-      instance: The name of the Skia Gold instance to interact with. It None,
+      instance: The name of the Skia Gold instance to interact with. If None,
           will use whatever default the subclass sets.
+      bucket: Overrides the formulaic Google Storage bucket name generated by
+          goldctl
     """
     instance = instance or self._GetDefaultInstance()
     keys_dict = _GetKeysAsDict(keys_input)
@@ -53,7 +59,7 @@
       working_dir = tempfile.mkdtemp(dir=self._working_dir)
       keys_file = _GetKeysAsJson(keys_input, working_dir)
       session = self.GetSessionClass()(working_dir, self._gold_properties,
-                                       keys_file, corpus, instance)
+                                       keys_file, corpus, instance, bucket)
       self._sessions[instance][corpus][keys_string] = session
     return session
 
diff --git a/build/skia_gold_common/skia_gold_session_unittest.py b/build/skia_gold_common/skia_gold_session_unittest.py
index 302210e..76c07999 100755
--- a/build/skia_gold_common/skia_gold_session_unittest.py
+++ b/build/skia_gold_common/skia_gold_session_unittest.py
@@ -423,13 +423,15 @@
                                                 sgp,
                                                 self._json_keys,
                                                 'corpus',
-                                                instance='instance')
+                                                instance='instance',
+                                                bucket='bucket')
     session.Initialize()
     call_args = cmd_mock.call_args[0][0]
     self.assertIn('imgtest', call_args)
     self.assertIn('init', call_args)
     self.assertIn('--passfail', call_args)
     assertArgWith(self, call_args, '--instance', 'instance')
+    assertArgWith(self, call_args, '--bucket', 'bucket')
     assertArgWith(self, call_args, '--corpus', 'corpus')
     # The keys file should have been copied to the working directory.
     assertArgWith(self, call_args, '--keys-file',
diff --git a/buildtools/DEPS b/buildtools/DEPS
index 62f132b..ec68f93 100644
--- a/buildtools/DEPS
+++ b/buildtools/DEPS
@@ -20,7 +20,7 @@
   'checkout_reclient': False,
 
   # reclient CIPD package version
-  'reclient_version': 're_client_version:0.19.3.3b3042c',
+  'reclient_version': 're_client_version:0.20.1.c4bbd2f',
 
   # When changing these, also update the svn revisions in deps_revisions.gni
   # TODO(crbug.com/1166332) rename to clang_format_revision.
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index 9aa6819..b440d3b 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -29,7 +29,9 @@
 #include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
 #include "components/viz/common/resources/shared_bitmap.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/gl_renderer.h"
 #include "components/viz/service/display/output_surface_client.h"
 #include "components/viz/service/display/software_output_device.h"
@@ -266,10 +268,6 @@
   output_surface_->BindToClient(output_surface_client_.get());
 
   shared_bitmap_manager_ = std::make_unique<viz::TestSharedBitmapManager>();
-  resource_provider_ = std::make_unique<viz::DisplayResourceProvider>(
-      viz::DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-      shared_bitmap_manager_.get());
-
   child_context_provider_ =
       base::MakeRefCounted<viz::TestInProcessContextProvider>(
           /*enable_gpu_rasterization=*/false,
@@ -281,9 +279,12 @@
 
 void PixelTest::SetUpGLRenderer(gfx::SurfaceOrigin output_surface_origin) {
   SetUpGLWithoutRenderer(output_surface_origin);
+  auto resource_provider = std::make_unique<viz::DisplayResourceProviderGL>(
+      output_surface_->context_provider(), shared_bitmap_manager_.get());
   renderer_ = std::make_unique<viz::GLRenderer>(
       &renderer_settings_, &debug_settings_, output_surface_.get(),
-      resource_provider_.get(), nullptr, base::ThreadTaskRunnerHandle::Get());
+      resource_provider.get(), nullptr, base::ThreadTaskRunnerHandle::Get());
+  resource_provider_ = std::move(resource_provider);
   renderer_->Initialize();
   renderer_->SetVisible(true);
 }
@@ -303,14 +304,13 @@
   output_surface_->BindToClient(output_surface_client_.get());
   static_cast<viz::SkiaOutputSurfaceImpl*>(output_surface_.get())
       ->SetCapabilitiesForTesting(output_surface_origin);
-  resource_provider_ = std::make_unique<viz::DisplayResourceProvider>(
-      viz::DisplayResourceProvider::kGpu,
-      /*compositor_context_provider=*/nullptr,
+  auto resource_provider = std::make_unique<viz::DisplayResourceProviderSkia>(
       /*shared_bitmap_manager=*/nullptr);
   renderer_ = std::make_unique<viz::SkiaRenderer>(
       &renderer_settings_, &debug_settings_, output_surface_.get(),
-      resource_provider_.get(), nullptr,
+      resource_provider.get(), nullptr,
       static_cast<viz::SkiaOutputSurface*>(output_surface_.get()));
+  resource_provider_ = std::move(resource_provider);
   renderer_->Initialize();
   renderer_->SetVisible(true);
 
@@ -346,14 +346,15 @@
       std::make_unique<viz::SoftwareOutputDevice>()));
   output_surface_->BindToClient(output_surface_client_.get());
   shared_bitmap_manager_ = std::make_unique<viz::TestSharedBitmapManager>();
-  resource_provider_ = std::make_unique<viz::DisplayResourceProvider>(
-      viz::DisplayResourceProvider::kSoftware, nullptr,
-      shared_bitmap_manager_.get());
+  auto resource_provider =
+      std::make_unique<viz::DisplayResourceProviderSoftware>(
+          shared_bitmap_manager_.get());
   child_resource_provider_ = std::make_unique<viz::ClientResourceProvider>();
 
   auto renderer = std::make_unique<viz::SoftwareRenderer>(
       &renderer_settings_, &debug_settings_, output_surface_.get(),
-      resource_provider_.get(), nullptr);
+      resource_provider.get(), nullptr);
+  resource_provider_ = std::move(resource_provider);
   software_renderer_ = renderer.get();
   renderer_ = std::move(renderer);
   renderer_->Initialize();
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index c40a958..3488232 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1838,7 +1838,7 @@
     }
 
     android_assets("${_variant}_locale_pak_assets") {
-      disable_compression = _is_bundle_module
+      disable_compression = true
       renaming_sources = []
       renaming_destinations = []
 
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index 1b632a59..7733ffc 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -406,6 +406,15 @@
         android:theme="@style/Theme.Chromium.TabbedMode"
         android:windowSoftInputMode="adjustResize">
     </activity>  # DIFF-ANCHOR: 9023f153
+    <activity  # DIFF-ANCHOR: e86e2b49
+        android:autoRemoveFromRecents="true"
+        android:documentLaunchMode="always"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$TrampolineActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay">
+    </activity>  # DIFF-ANCHOR: e86e2b49
     <activity  # DIFF-ANCHOR: bec48e1f
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
         android:enabled="false"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index 61f7bfa6..24d31898 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -379,6 +379,15 @@
         android:theme="@style/Theme.Chromium.TabbedMode"
         android:windowSoftInputMode="adjustResize">
     </activity>  # DIFF-ANCHOR: 9023f153
+    <activity  # DIFF-ANCHOR: e86e2b49
+        android:autoRemoveFromRecents="true"
+        android:documentLaunchMode="always"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$TrampolineActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay">
+    </activity>  # DIFF-ANCHOR: e86e2b49
     <activity  # DIFF-ANCHOR: bec48e1f
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
         android:enabled="false"
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureSingleRunTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureSingleRunTest.java
index dbe7e147..01e32ed 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureSingleRunTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureSingleRunTest.java
@@ -249,6 +249,26 @@
         logPasswordStoreCredentials(mPasswordStoreBridge, "Final password store state");
     }
 
+    /**
+     * Runs the password change flow for all credentials in the store and validates the changes.
+     */
+    @Test
+    @Manual
+    public void testMultipleCredentials() throws Exception {
+        for (int run = 0; run < mParameters.getNumRuns(); run++) {
+            // Maintain a reference to the current set of credentials.
+            PasswordStoreCredential[] initialCredentials = mCredentials;
+            // Run password change for all credentials.
+            for (int i = 0; i < initialCredentials.length; i++) {
+                // Run and test script.
+                testSingleRun(initialCredentials[i].getUsername());
+            }
+            Log.i(TAG, "[EVENT: Run #%s succeded]", String.valueOf(run + 1));
+        }
+
+        logPasswordStoreCredentials(mPasswordStoreBridge, "Final password store state");
+    }
+
     @Override
     public void onSavedPasswordsChanged(int count) {
         logPasswordStoreCredentials(
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index e94055e..9850609d 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -74,15 +75,17 @@
 
     /**
      * Starts Autofill Assistant.
-     * @param activity {@link ChromeActivity} the activity on which the Autofill Assistant is being
+     * @param activity {@link Activity} the activity on which the Autofill Assistant is being
      *         started.
      * @param bundleExtras {@link Bundle} the extras which were used to start the Autofill
      *         Assistant.
      * @param initialUrl the initial URL the Autofill Assistant should be started on.
      */
-    public static void start(
-            ChromeActivity activity, @Nullable Bundle bundleExtras, String initialUrl) {
-        start(activity,
+    public static void start(Activity activity, @Nullable Bundle bundleExtras, String initialUrl) {
+        // TODO(crbug.com/1155809): Remove ChromeActivity reference.
+        assert activity instanceof ChromeActivity;
+        ChromeActivity chromeActivity = (ChromeActivity) activity;
+        start(chromeActivity,
                 AutofillAssistantArguments.newBuilder()
                         .fromBundle(bundleExtras)
                         .withInitialUrl(initialUrl)
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index 333f689..b56b59e 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -46,6 +46,8 @@
 import android.widget.ImageView;
 
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
 import androidx.test.espresso.contrib.RecyclerViewActions;
 import androidx.test.espresso.matcher.BoundedMatcher;
 import androidx.test.filters.SmallTest;
@@ -126,6 +128,8 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
@@ -171,6 +175,9 @@
     @Rule
     public ErrorCollector collector = new ErrorCollector();
 
+    @Rule
+    public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule();
+
     /**
      * Parameter set controlling whether Feed v2 is enabled.
      */
@@ -680,7 +687,7 @@
         CriteriaHelper.pollUiThread(() -> allCardsHaveThumbnail(recyclerView));
         // TODO(crbug.com/1065314): Tab group cards should not have favicons.
         mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view),
-                "tabSwitcher_tabGroups");
+                "tabSwitcher_tabGroups_aspect_ratio_point85");
 
         // Resume native initialization and make sure the GTS looks the same.
         startAndWaitNativeInitialization();
@@ -1123,18 +1130,7 @@
                     "/exclude_mv_tiles/false"})
     public void renderSingleAsHomepage_MVTiles() throws IOException, InterruptedException {
         // clang-format on
-        // Get old file and ensure to delete it.
-        File oldFile = MostVisitedSitesMetadataUtils.getOrCreateTopSitesDirectory();
-        Assert.assertTrue(oldFile.delete() && !oldFile.exists());
-
-        // Save suggestion lists to file.
-        final CountDownLatch latch = new CountDownLatch(1);
-        MostVisitedSitesMetadataUtils.saveSuggestionListsToFile(
-                createFakeSiteSuggestionTiles(), latch::countDown);
-
-        // Wait util the file has been saved.
-        latch.await();
-
+        saveSiteSuggestionTilesToFile();
         startMainActivityFromLauncher();
         CriteriaHelper.pollUiThread(
                 () -> mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
@@ -1145,7 +1141,7 @@
         ViewUtils.onViewWaiting(
                 allOf(withId(R.id.tile_view_title), withText("0 TOP_SITES"), isDisplayed()));
         ChromeRenderTestRule.sanitize(surface);
-        mRenderTestRule.render(surface, "singlePane_MV");
+        mRenderTestRule.render(surface, "singlePane_MV_withTopSitesView");
     }
 
     @Test
@@ -1213,6 +1209,63 @@
         });
     }
 
+    @Test
+    @SmallTest
+    @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
+    // clang-format off
+    @EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,",
+            ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.EXPLORE_SITES})
+    @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
+            "force-fieldtrials=Study/Group",
+            IMMEDIATE_RETURN_PARAMS +
+                    "/start_surface_variation/single/exclude_mv_tiles/false"})
+    public void testExploreTopSites() throws InterruptedException {
+        // clang-format on
+        // When showing MV tiles pre-native, explore top sites view is already rendered with a
+        // non-null icon. This test is for ensuring explore top sites view is built and clickable
+        // after native initialization.
+        saveSiteSuggestionTilesToFile();
+
+        FakeMostVisitedSites mostVisitedSites = new FakeMostVisitedSites();
+        mostVisitedSites.setTileSuggestions(createFakeSiteSuggestions());
+        mSuggestionsDeps.getFactory().mostVisitedSites = mostVisitedSites;
+
+        startMainActivityFromLauncher();
+        CriteriaHelper.pollUiThread(
+                () -> mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
+
+        // Initializes native.
+        startAndWaitNativeInitialization();
+
+        waitForTabModel();
+        assertEquals(0,
+                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel().getCount());
+
+        OverviewModeBehaviorWatcher hideWatcher =
+                TabUiTestHelper.createOverviewHideWatcher(mActivityTestRule.getActivity());
+        onViewWaiting(withId(org.chromium.chrome.tab_ui.R.id.mv_tiles_layout))
+                .perform(new ViewAction() {
+                    @Override
+                    public Matcher<View> getConstraints() {
+                        return isDisplayed();
+                    }
+
+                    @Override
+                    public String getDescription() {
+                        return "Click explore top sites view in MV tiles.";
+                    }
+
+                    @Override
+                    public void perform(UiController uiController, View view) {
+                        ViewGroup mvTilesContainer = (ViewGroup) view;
+                        mvTilesContainer.getChildAt(1).performClick();
+                    }
+                });
+        hideWatcher.waitForBehavior();
+        assertEquals(1,
+                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel().getCount());
+    }
+
     /**
      * Toggles the header and checks whether the header has the right status.
      *
@@ -1273,17 +1326,38 @@
     }
 
     private static List<Tile> createFakeSiteSuggestionTiles() {
+        List<SiteSuggestion> siteSuggestions = createFakeSiteSuggestions();
         List<Tile> suggestionTiles = new ArrayList<>();
-        SiteSuggestion data = new SiteSuggestion("0 TOP_SITES", new GURL("https://www.foo.com"), "",
-                TileTitleSource.TITLE_TAG, TileSource.TOP_SITES, TileSectionType.PERSONALIZED,
-                new Date());
-        suggestionTiles.add(new Tile(data, 0));
-
-        data = new SiteSuggestion("1 ALLOWLIST", new GURL("https://www.bar.com"), "",
-                TileTitleSource.UNKNOWN, TileSource.ALLOWLIST, TileSectionType.PERSONALIZED,
-                new Date());
-        suggestionTiles.add(new Tile(data, 1));
-
+        for (int i = 0; i < siteSuggestions.size(); i++) {
+            suggestionTiles.add(new Tile(siteSuggestions.get(i), i));
+        }
         return suggestionTiles;
     }
+
+    private static List<SiteSuggestion> createFakeSiteSuggestions() {
+        List<SiteSuggestion> siteSuggestions = new ArrayList<>();
+        siteSuggestions.add(new SiteSuggestion("0 TOP_SITES", new GURL("https://www.foo.com"), "",
+                TileTitleSource.TITLE_TAG, TileSource.TOP_SITES, TileSectionType.PERSONALIZED,
+                new Date()));
+
+        siteSuggestions.add(new SiteSuggestion("1 ALLOWLIST", new GURL("https://www.bar.com"), "",
+                TileTitleSource.UNKNOWN, TileSource.EXPLORE, TileSectionType.PERSONALIZED,
+                new Date()));
+
+        return siteSuggestions;
+    }
+
+    private static void saveSiteSuggestionTilesToFile() throws InterruptedException {
+        // Get old file and ensure to delete it.
+        File oldFile = MostVisitedSitesMetadataUtils.getOrCreateTopSitesDirectory();
+        Assert.assertTrue(oldFile.delete() && !oldFile.exists());
+
+        // Save suggestion lists to file.
+        final CountDownLatch latch = new CountDownLatch(1);
+        MostVisitedSitesMetadataUtils.saveSuggestionListsToFile(
+                createFakeSiteSuggestionTiles(), latch::countDown);
+
+        // Wait util the file has been saved.
+        latch.await();
+    }
 }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index 98a80dc..0ce8c3aa 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -182,7 +182,7 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
 
     @SuppressWarnings("FieldCanBeLocal")
     private EmbeddedTestServer mTestServer;
@@ -731,7 +731,6 @@
     @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M,
             message = "https://crbug.com/1023833")
     public void testIncognitoToggle_thumbnailFetchCount() throws InterruptedException {
-        mActivityTestRule.loadUrl(mUrl);
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         int oldFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
 
@@ -820,7 +819,8 @@
     @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study")
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0/"
+        + "thumbnail_aspect_ratio/1.0"})
     public void testTabSuggestionMessageCard_dismiss() throws InterruptedException {
         // clang-format on
         prepareTabs(3, 0, null);
@@ -850,7 +850,8 @@
     @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study")
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"
+        + "/thumbnail_aspect_ratio/1.0"})
     public void testTabSuggestionMessageCard_review() throws InterruptedException {
         // clang-format on
         prepareTabs(3, 0, null);
@@ -880,7 +881,8 @@
     @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"})
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0/"
+        + "thumbnail_aspect_ratio/1.0"})
     public void testShowOnlyOneTabSuggestionMessageCard_withSoftCleanup()
             throws InterruptedException {
         // clang-format on
@@ -893,7 +895,8 @@
     @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"})
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0/"
+        + "thumbnail_aspect_ratio/1.0"})
     public void testShowOnlyOneTabSuggestionMessageCard_withHardCleanup()
             throws InterruptedException {
         // clang-format on
@@ -906,7 +909,8 @@
     @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"})
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0/"
+        + "thumbnail_aspect_ratio/1.0"})
     public void testTabSuggestionMessageCardDismissAfterTabClosing() throws InterruptedException {
         // clang-format on
         prepareTabs(3, 0, mUrl);
@@ -992,7 +996,8 @@
     @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study")
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0/"
+        + "thumbnail_aspect_ratio/1.0"})
     public void testTabSuggestionMessageCard_orientation() throws InterruptedException {
         // clang-format on
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
@@ -1326,8 +1331,8 @@
     @Feature({"RenderTest"})
     // clang-format off
     @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
-    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.85"})
-    public void testRenderGrid_withAspectRatioPoint85() throws IOException {
+    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/1.0"})
+    public void testRenderGrid_withAspectRatioOfOne() throws IOException {
         // clang-format on
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         prepareTabs(3, 0, "about:blank");
@@ -1337,7 +1342,7 @@
         createTabGroup(cta, false, tabGroup);
         // Make sure all tabs have thumbnail.
         enterGTSWithThumbnailRetry();
-        mRenderTestRule.render(cta.findViewById(R.id.tab_list_view), "aspect_ratio_point_85");
+        mRenderTestRule.render(cta.findViewById(R.id.tab_list_view), "aspect_ratio_of_one");
     }
 
     @Test
@@ -1804,12 +1809,14 @@
 
     @Test
     @MediumTest
+    @Feature("TabSuggestion")
     // clang-format off
     @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
             ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"})
     @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study")
     @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true" +
-            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0"})
+            "/baseline_close_tab_suggestions/true/min_time_between_prefetches/0" +
+            "/thumbnail_aspect_ratio/1.0"})
     public void testTabGroupManualSelection_AfterReviewTabSuggestion() throws InterruptedException {
         // clang-format on
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
index 64f9067..aff0fd4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
@@ -8,6 +8,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.chromium.base.Log;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
@@ -45,6 +46,7 @@
  * TODO(mattsimmons): Move logic and view manipulation into the mediator/viewbinder. (and add tests)
  */
 class MostVisitedListCoordinator implements TileGroup.Observer, TileGroup.TileSetupDelegate {
+    private static final String TAG = "TopSites";
     private static final int TITLE_LINES = 1;
 
     // There's a limit of 12 in {@link MostVisitedSitesBridge#setObserver}.
@@ -83,7 +85,7 @@
                     mRenderer.renderTileSection(tiles, mParent, this);
                 }
             } catch (IOException e) {
-                e.printStackTrace();
+                Log.i(TAG, "No cached MV tiles file.");
             }
         }
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
index 3c18079..268b4ab8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
@@ -47,13 +47,6 @@
      */
     public interface PriceWelcomeMessageProvider {
         /**
-         * This method gets the information of the first tab showing price card.
-         *
-         * @return The PriceTabData including the tab ID and the price drop.
-         */
-        PriceTabData getFirstTabShowingPriceCard();
-
-        /**
          * This method gets the tab index from tab ID.
          *
          * @param tabId The tab ID to search for.
@@ -75,11 +68,11 @@
      */
     public interface PriceWelcomeMessageReviewActionProvider {
         /**
-         * This method scrolls to the binding tab of the PriceWelcomeMessage.
+         * This method scrolls to the tab at given index.
          *
-         * @param tabIndex The index of the {@link Tab} that is binding to PriceWelcomeMessage.
+         * @param tabIndex The index of the {@link Tab} which we will scroll to.
          */
-        void scrollToBindingTab(int tabIndex);
+        void scrollToTab(int tabIndex);
     }
 
     /**
@@ -144,13 +137,8 @@
         mPriceWelcomeMessageReviewActionProvider = priceWelcomeMessageReviewActionProvider;
     }
 
-    void preparePriceMessage() {
-        if (PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled()) return;
-        PriceTabData priceTabData = mPriceWelcomeMessageProvider.getFirstTabShowingPriceCard();
-        if (priceTabData == null) {
-            invalidateMessage();
-            return;
-        }
+    void preparePriceMessage(PriceTabData priceTabData) {
+        assert priceTabData != null;
         PriceTrackingUtilities.increasePriceWelcomeMessageCardShowCount();
         if (PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount()
                 > MAX_PRICE_WELCOME_MESSAGE_SHOW_COUNT
@@ -180,14 +168,16 @@
         assert mPriceTabData != null;
         int bindingTabIndex =
                 mPriceWelcomeMessageProvider.getTabIndexFromTabId(mPriceTabData.bindingTabId);
-        mPriceWelcomeMessageReviewActionProvider.scrollToBindingTab(bindingTabIndex);
+        mPriceWelcomeMessageReviewActionProvider.scrollToTab(bindingTabIndex);
         mPriceWelcomeMessageProvider.showPriceDropTooltip(bindingTabIndex);
         PriceTrackingUtilities.disablePriceWelcomeMessageCard();
+        mPriceTabData = null;
     }
 
     @VisibleForTesting
     public void dismiss() {
         PriceTrackingUtilities.disablePriceWelcomeMessageCard();
+        mPriceTabData = null;
     }
 
     @VisibleForTesting
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index bc45ef7..02f127c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -82,7 +82,7 @@
                         : TabListCoordinator.TabListMode.GRID,
                 context, tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null,
                 false, gridCardOnClickListenerProvider, mMediator.getTabGridDialogHandler(),
-                TabProperties.UiType.CLOSABLE, null, containerView, false, mComponentName);
+                TabProperties.UiType.CLOSABLE, null, null, containerView, false, mComponentName);
         TabListRecyclerView recyclerView = mTabListCoordinator.getContainerView();
 
         TabGroupUiToolbarView toolbarView =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index a49a25e..c61dae8d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -226,20 +226,15 @@
                             if (shoppingPersistedTabData == null
                                     || shoppingPersistedTabData.getPriceDrop() == null) {
                                 priceCardView.setVisibility(View.GONE);
-                                model.set(TabProperties.PRICE_DROP, null);
                             } else {
                                 priceCardView.setPriceStrings(
                                         shoppingPersistedTabData.getPriceDrop().price,
                                         shoppingPersistedTabData.getPriceDrop().previousPrice);
                                 priceCardView.setVisibility(View.VISIBLE);
-                                model.set(TabProperties.PRICE_DROP,
-                                        shoppingPersistedTabData.getPriceDrop());
                             }
                         });
             } else {
                 priceCardView.setVisibility(View.GONE);
-                // TODO(crbug.com/1157578): Update the model in mediator.
-                model.set(TabProperties.PRICE_DROP, null);
             }
         } else if (TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP == propertyKey) {
             if (model.get(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP)) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index ea05d9d..acbc104 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -102,7 +102,8 @@
         boolean actionOnAllRelatedTabs = TabUiFeatureUtilities.isConditionalTabStripEnabled();
         mTabStripCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.STRIP,
                 mContext, tabModelSelector, null, null, actionOnAllRelatedTabs, null, null,
-                TabProperties.UiType.STRIP, null, mTabListContainerView, true, COMPONENT_NAME);
+                TabProperties.UiType.STRIP, null, null, mTabListContainerView, true,
+                COMPONENT_NAME);
         mTabStripCoordinator.initWithNative(
                 mActivity.getCompositorViewHolder().getDynamicResourceLoader());
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 8a19869..d6c02fd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
-import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.chrome.tab_ui.R;
@@ -100,6 +99,7 @@
      * @param itemType The item type to put in the list of tabs.
      * @param selectionDelegateProvider Provider to provide selected Tabs for a selectable tab list.
      *                                  It's NULL when selection is not possible.
+     * @param priceWelcomeMessageController A controller to show PriceWelcomeMessage.
      * @param parentView {@link ViewGroup} The root view of the UI.
      * @param attachToParent Whether the UI should attach to root view.
      * @param componentName A unique string uses to identify different components for UMA recording.
@@ -113,6 +113,8 @@
                     .GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
             @Nullable TabListMediator.TabGridDialogHandler dialogHandler, @UiType int itemType,
             @Nullable TabListMediator.SelectionDelegateProvider selectionDelegateProvider,
+            @Nullable TabSwitcherMediator
+                    .PriceWelcomeMessageController priceWelcomeMessageController,
             @NonNull ViewGroup parentView, boolean attachToParent, String componentName) {
         mMode = mode;
         mItemType = itemType;
@@ -241,7 +243,7 @@
         mMediator = new TabListMediator(context, mModel, mMode, tabModelSelector, thumbnailProvider,
                 titleProvider, tabListFaviconProvider, actionOnRelatedTabs,
                 selectionDelegateProvider, gridCardOnClickListenerProvider, dialogHandler,
-                componentName, itemType);
+                priceWelcomeMessageController, componentName, itemType);
 
         if (mMode == TabListMode.GRID) {
             GridLayoutManager gridLayoutManager =
@@ -456,11 +458,6 @@
 
     // PriceWelcomeMessageService.PriceWelcomeMessageProvider implementation.
     @Override
-    public PriceTabData getFirstTabShowingPriceCard() {
-        return mModel.getFirstTabShowingPriceCard();
-    }
-
-    @Override
     public int getTabIndexFromTabId(int tabId) {
         return mModel.indexFromId(tabId);
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index f3d15cf..36e51b6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -61,8 +61,10 @@
 import org.chromium.chrome.browser.tasks.tab_groups.EmptyTabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
+import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
+import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMediator.PriceWelcomeMessageController;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -171,12 +173,20 @@
      */
     static class ShoppingPersistedTabDataFetcher {
         protected Tab mTab;
+        protected TabListModel mModel;
+        protected PriceWelcomeMessageController mPriceWelcomeMessageController;
 
         /**
-         * @param tab {@link Tab} {@link ShoppingPersistedTabData} will be acquired for
+         * @param tab {@link Tab} {@link ShoppingPersistedTabData} will be acquired for.
+         * @param model {@link TabListModel} to check if we already have the price welcome message
+         *         in the model.
+         * @param priceWelcomeMessageController to show the price welcome message.
          */
-        ShoppingPersistedTabDataFetcher(Tab tab) {
+        ShoppingPersistedTabDataFetcher(Tab tab, @Nullable TabListModel model,
+                @Nullable PriceWelcomeMessageController priceWelcomeMessageController) {
             mTab = tab;
+            mModel = model;
+            mPriceWelcomeMessageController = priceWelcomeMessageController;
         }
 
         /**
@@ -184,7 +194,29 @@
          * @param callback {@link Callback} to pass {@link ShoppingPersistedTabData} back in
          */
         public void fetch(Callback<ShoppingPersistedTabData> callback) {
-            ShoppingPersistedTabData.from(mTab, (res) -> { callback.onResult(res); });
+            ShoppingPersistedTabData.from(mTab, (res) -> {
+                callback.onResult(res);
+                maybeShowPriceWelcomeMessage(res);
+            });
+        }
+
+        @VisibleForTesting
+        void maybeShowPriceWelcomeMessage(
+                @Nullable ShoppingPersistedTabData shoppingPersistedTabData) {
+            // Avoid inserting message while RecyclerView is computing a layout.
+            new Handler().post(() -> {
+                if (PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled() || (mModel == null)
+                        || (mPriceWelcomeMessageController == null)
+                        || (shoppingPersistedTabData == null)
+                        || (shoppingPersistedTabData.getPriceDrop() == null)
+                        || (mModel.lastIndexForMessageItemFromType(
+                                    MessageService.MessageType.PRICE_WELCOME)
+                                != TabModel.INVALID_TAB_INDEX)) {
+                    return;
+                }
+                mPriceWelcomeMessageController.showPriceWelcomeMessage(
+                        new PriceTabData(mTab.getId(), shoppingPersistedTabData.getPriceDrop()));
+            });
         }
     }
 
@@ -291,6 +323,7 @@
     private final TabGridDialogHandler mTabGridDialogHandler;
     private final String mComponentName;
     private final TabListFaviconProvider mTabListFaviconProvider;
+    private final PriceWelcomeMessageController mPriceWelcomeMessageController;
 
     private ThumbnailProvider mThumbnailProvider;
     private boolean mActionsOnAllRelatedTabs;
@@ -467,6 +500,7 @@
      * @param gridCardOnClickListenerProvider Provides the onClickListener for opening dialog when
      *                                        click on a grid card.
      * @param dialogHandler A handler to handle requests about updating TabGridDialog.
+     * @param priceWelcomeMessageController A controller to show PriceWelcomeMessage.
      * @param componentName This is a unique string to identify different components.
      * @param uiType The type of UI this mediator should be building.
      */
@@ -476,8 +510,9 @@
             TabListFaviconProvider tabListFaviconProvider, boolean actionOnRelatedTabs,
             @Nullable SelectionDelegateProvider selectionDelegateProvider,
             @Nullable GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
-            @Nullable TabGridDialogHandler dialogHandler, String componentName,
-            @UiType int uiType) {
+            @Nullable TabGridDialogHandler dialogHandler,
+            @Nullable PriceWelcomeMessageController priceWelcomeMessageController,
+            String componentName, @UiType int uiType) {
         mContext = context;
         mTabModelSelector = tabModelSelector;
         mThumbnailProvider = thumbnailProvider;
@@ -491,6 +526,7 @@
         mTabGridDialogHandler = dialogHandler;
         mActionsOnAllRelatedTabs = actionOnRelatedTabs;
         mUiType = uiType;
+        mPriceWelcomeMessageController = priceWelcomeMessageController;
 
         mTabModelObserver = new TabModelObserver() {
             @Override
@@ -1116,7 +1152,8 @@
         if (mMode == TabListMode.GRID && pseudoTab.hasRealTab() && !pseudoTab.isIncognito()
                 && PriceTrackingUtilities.isTrackPricesOnTabsEnabled()) {
             mModel.get(index).model.set(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER,
-                    new ShoppingPersistedTabDataFetcher(pseudoTab.getTab()));
+                    new ShoppingPersistedTabDataFetcher(
+                            pseudoTab.getTab(), mModel, mPriceWelcomeMessageController));
         } else {
             mModel.get(index).model.set(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER, null);
         }
@@ -1331,7 +1368,6 @@
                         .with(TabProperties.TABSTRIP_FAVICON_BACKGROUND_COLOR_ID,
                                 tabstripFaviconBackgroundDrawableId)
                         .with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate)
-                        .with(TabProperties.PRICE_DROP, null)
                         .with(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, false)
                         .with(CARD_TYPE, TAB)
                         .build();
@@ -1649,7 +1685,8 @@
     int getPriceWelcomeMessageInsertionIndex() {
         assert mGridLayoutManager != null;
         int spanCount = mGridLayoutManager.getSpanCount();
-        int selectedTabIndex = mModel.getIndexForSelectedTab();
+        int selectedTabIndex = mModel.indexOfNthTabCard(
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index());
         int indexBelowSelectedTab = (selectedTabIndex / spanCount + 1) * spanCount;
         int indexAfterLastTab = mModel.getTabIndexBefore(mModel.size()) + 1;
         return Math.min(indexBelowSelectedTab, indexAfterLastTab);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
index e230557..2a42b92 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -19,7 +19,6 @@
 
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
 import org.chromium.ui.modelutil.MVCListAdapter;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyListModel;
@@ -131,28 +130,6 @@
     }
 
     /**
-     * Get the index of currently selected tab in TabListModel.
-     * @return The index within the model.
-     */
-    public int getIndexForSelectedTab() {
-        int selectedTabCount = 0;
-        int tabCount = 0;
-        int firstSelectedTabIndex = TabModel.INVALID_TAB_INDEX;
-        for (int i = size() - 1; i >= 0; i--) {
-            PropertyModel model = get(i).model;
-            if (model.get(CARD_TYPE) != TAB) continue;
-            if (model.get(TabProperties.IS_SELECTED)) {
-                selectedTabCount++;
-                firstSelectedTabIndex = i;
-            }
-            tabCount++;
-        }
-        assert (selectedTabCount == 1 || tabCount == 0)
-            : "There should be exactly one selected tab or no tabs at all when calling this method";
-        return firstSelectedTabIndex;
-    }
-
-    /**
      * Get the index that matches a message item that has the given message type.
      * @param messageType The message type to match.
      * @return The index within the model.
@@ -286,18 +263,4 @@
 
         get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status);
     }
-
-    /**
-     * Get the first tab showing price card.
-     */
-    public PriceTabData getFirstTabShowingPriceCard() {
-        for (int i = 0; i < size(); i++) {
-            PropertyModel model = get(i).model;
-            if ((model.get(CARD_TYPE) == TAB) && (model.get(TabProperties.PRICE_DROP) != null)) {
-                return new PriceTabData(
-                        model.get(TabProperties.TAB_ID), model.get(TabProperties.PRICE_DROP));
-            }
-        }
-        return null;
-    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
index c27e915..b7a08ca 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
@@ -13,7 +13,6 @@
 
 import androidx.annotation.IntDef;
 
-import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -119,10 +118,6 @@
 
     public static final WritableObjectPropertyKey<String> CLOSE_BUTTON_DESCRIPTION_STRING =
             new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<ShoppingPersistedTabData.PriceDrop> PRICE_DROP =
-            new WritableObjectPropertyKey<>();
-
     public static final WritableBooleanPropertyKey SHOULD_SHOW_PRICE_DROP_TOOLTIP =
             new WritableBooleanPropertyKey();
 
@@ -135,7 +130,7 @@
             SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL_DOMAIN, ACCESSIBILITY_DELEGATE,
             SEARCH_QUERY, PAGE_INFO_LISTENER, PAGE_INFO_ICON_DRAWABLE_ID, CARD_TYPE,
             CONTENT_DESCRIPTION_STRING, CLOSE_BUTTON_DESCRIPTION_STRING,
-            SHOPPING_PERSISTED_TAB_DATA_FETCHER, PRICE_DROP, SHOULD_SHOW_PRICE_DROP_TOOLTIP};
+            SHOPPING_PERSISTED_TAB_DATA_FETCHER, SHOULD_SHOW_PRICE_DROP_TOOLTIP};
 
     public static final PropertyKey[] ALL_KEYS_TAB_STRIP =
             new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
index e644141..4c6ca72f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
@@ -131,7 +131,7 @@
 
         mTabListCoordinator = new TabListCoordinator(mode, context, mTabModelSelector,
                 tabContentManager::getTabThumbnailWithCallback, null, false, null, null,
-                TabProperties.UiType.SELECTABLE, this::getSelectionDelegate,
+                TabProperties.UiType.SELECTABLE, this::getSelectionDelegate, null,
                 mTabSelectionEditorLayout, false, COMPONENT_NAME);
 
         // Note: The TabSelectionEditorCoordinator is always created after native is initialized.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 9ff1c51..695ca36 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -176,7 +176,7 @@
 
         mTabListCoordinator = new TabListCoordinator(mode, context, tabModelSelector,
                 mMultiThumbnailCardProvider, titleProvider, true, mMediator, null,
-                TabProperties.UiType.CLOSABLE, null, container, true, COMPONENT_NAME);
+                TabProperties.UiType.CLOSABLE, null, this, container, true, COMPONENT_NAME);
         mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
                 mTabListCoordinator.getContainerView(), TabListContainerViewBinder::bind);
 
@@ -461,6 +461,11 @@
     public boolean resetWithTabs(
             @Nullable List<PseudoTab> tabs, boolean quickMode, boolean mruMode) {
         mMediator.registerFirstMeaningfulPaintRecorder();
+        // Invalidate price welcome message for every reset so that the stale message won't be
+        // restored by mistake (e.g. from tabClosureUndone in TabSwitcherMediator).
+        if (mPriceWelcomeMessageService != null) {
+            mPriceWelcomeMessageService.invalidateMessage();
+        }
         boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode);
         if (showQuickly) {
             mTabListCoordinator.removeSpecialListItem(TabProperties.UiType.NEW_TAB_TILE, 0);
@@ -475,11 +480,6 @@
         }
 
         if (tabs != null && tabs.size() > 0) {
-            // TODO(crbug.com/1157578): Auto update the PriceWelcomeMessageService instead of
-            // updating it based on the client caller.
-            if (mPriceWelcomeMessageService != null) {
-                mPriceWelcomeMessageService.preparePriceMessage();
-            }
             appendMessagesTo(cardsCount);
         }
 
@@ -500,14 +500,11 @@
         List<MessageCardProviderMediator.Message> messages =
                 mMessageCardProviderCoordinator.getMessageItems();
         for (int i = 0; i < messages.size(); i++) {
-            if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
-                mTabListCoordinator.addSpecialListItem(
-                        mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
-                        TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
-            } else {
-                mTabListCoordinator.addSpecialListItemToEnd(
-                        TabProperties.UiType.MESSAGE, messages.get(i).model);
-            }
+            // The restore of PRICE_WELCOME message is handled in the restorePriceWelcomeMessage()
+            // below.
+            if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) continue;
+            mTabListCoordinator.addSpecialListItemToEnd(
+                    TabProperties.UiType.MESSAGE, messages.get(i).model);
         }
         sAppendedMessagesForTesting = messages.size() > 0;
     }
@@ -524,20 +521,31 @@
         appendNextMessage(MessageService.MessageType.PRICE_WELCOME);
     }
 
+    @Override
+    public void showPriceWelcomeMessage(PriceWelcomeMessageService.PriceTabData priceTabData) {
+        if (mPriceWelcomeMessageService == null
+                || PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled()) {
+            return;
+        }
+        mPriceWelcomeMessageService.preparePriceMessage(priceTabData);
+        appendNextMessage(MessageService.MessageType.PRICE_WELCOME);
+        // To make the message card in view when user enters tab switcher, we should scroll to
+        // current tab with 0 offset. See {@link TabSwitcherMediator#setInitialScrollIndexOffset}
+        // for more details.
+        mMediator.scrollToTab(
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index());
+    }
+
     private void appendMessagesTo(int index) {
         if (mMultiWindowModeStateDispatcher.isInMultiWindowMode()) return;
         sAppendedMessagesForTesting = false;
         List<MessageCardProviderMediator.Message> messages =
                 mMessageCardProviderCoordinator.getMessageItems();
         for (int i = 0; i < messages.size(); i++) {
-            if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
-                mTabListCoordinator.addSpecialListItem(
-                        mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
-                        TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
-            } else {
-                mTabListCoordinator.addSpecialListItem(
-                        index + i, TabProperties.UiType.MESSAGE, messages.get(i).model);
-            }
+            if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) continue;
+            mTabListCoordinator.addSpecialListItem(
+                    index, TabProperties.UiType.MESSAGE, messages.get(i).model);
+            index++;
         }
         if (messages.size() > 0) sAppendedMessagesForTesting = true;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 514b3d1f..7e934b3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -206,6 +206,12 @@
          * when the closure of the binding tab in tab switcher is undone.
          */
         void restorePriceWelcomeMessage();
+
+        /**
+         * Show the price welcome message in tab switcher. This is used when any open tab in tab
+         * switcher has a price drop.
+         */
+        void showPriceWelcomeMessage(PriceWelcomeMessageService.PriceTabData priceTabData);
     }
 
     /**
@@ -323,7 +329,8 @@
             public void tabClosureUndone(Tab tab) {
                 if (mTabModelSelector.getCurrentModel().getCount() == 1) {
                     messageItemsController.restoreAllAppendedMessage();
-                } else if (mPriceWelcomeMessageService != null
+                }
+                if (mPriceWelcomeMessageService != null
                         && mPriceWelcomeMessageService.getBindingTabId() == tab.getId()) {
                     priceWelcomeMessageController.restorePriceWelcomeMessage();
                 }
@@ -834,7 +841,7 @@
     }
 
     @Override
-    public void scrollToBindingTab(int tabIndex) {
+    public void scrollToTab(int tabIndex) {
         mContainerViewModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, tabIndex);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index f29b4de..b438954 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -44,7 +44,7 @@
     public static final String THUMBNAIL_ASPECT_RATIO_PARAM = "thumbnail_aspect_ratio";
     public static final DoubleCachedFieldTrialParameter THUMBNAIL_ASPECT_RATIO =
             new DoubleCachedFieldTrialParameter(
-                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, THUMBNAIL_ASPECT_RATIO_PARAM, 1.0);
+                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, THUMBNAIL_ASPECT_RATIO_PARAM, 0.85);
 
     private static final String SEARCH_CHIP_PARAM = "enable_search_term_chip";
     public static final BooleanCachedFieldTrialParameter ENABLE_SEARCH_CHIP =
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index ef529aa4..b3e3674 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -43,7 +43,6 @@
 import org.chromium.chrome.browser.tab.state.LevelDBPersistedDataStorage;
 import org.chromium.chrome.browser.tab.state.LevelDBPersistedDataStorageJni;
 import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
-import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData.PriceDrop;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -686,8 +685,6 @@
         mGridModel.set(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER, null);
         PriceCardView priceCardView = mTabGridView.findViewById(R.id.price_info_box_outer);
         Assert.assertEquals(View.GONE, priceCardView.getVisibility());
-        // TODO(crbug.com/1157578): Update the model in mediator.
-        Assert.assertNull(mGridModel.get(TabProperties.PRICE_DROP));
     }
 
     private void testPriceString(Tab tab, MockShoppingPersistedTabDataFetcher fetcher,
@@ -704,7 +701,6 @@
             Assert.assertEquals(expectedCurrentPrice, currentPrice.getText());
             Assert.assertEquals(expectedPreviousPrice, previousPrice.getText());
         }
-        Assert.assertEquals(fetcher.getPriceDrop(), mGridModel.get(TabProperties.PRICE_DROP));
     }
 
     static class MockShoppingPersistedTabData extends ShoppingPersistedTabData {
@@ -731,7 +727,7 @@
             extends TabListMediator.ShoppingPersistedTabDataFetcher {
         private ShoppingPersistedTabData mShoppingPersistedTabData;
         MockShoppingPersistedTabDataFetcher(Tab tab) {
-            super(tab);
+            super(tab, null, null);
         }
 
         public void setPriceStrings(String priceString, String previousPriceString) {
@@ -744,11 +740,6 @@
             mShoppingPersistedTabData = new MockShoppingPersistedTabData(mTab);
         }
 
-        public PriceDrop getPriceDrop() {
-            if (mShoppingPersistedTabData == null) return null;
-            return ((MockShoppingPersistedTabData) mShoppingPersistedTabData).getPriceDrop();
-        }
-
         @Override
         public void fetch(Callback<ShoppingPersistedTabData> callback) {
             callback.onResult(mShoppingPersistedTabData);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
index fd12762f..fd8d80c 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
@@ -68,7 +68,7 @@
     // clang-format on
     private static final String BASE_PARAMS = "force-fieldtrial-params="
             + "Study.Group:baseline_tab_suggestions/true/enable_launch_polish/true"
-            + "/min_time_between_prefetches/0";
+            + "/min_time_between_prefetches/0/thumbnail_aspect_ratio/1.0";
     private static final String ENABLE_CLOSE_SUGGESTION_PARAM =
             "/baseline_close_tab_suggestions/true";
     private static final String ENABLE_GROUP_SUGGESTION_PARAM =
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
index 26b3a7d..ba5d73bc 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
@@ -75,7 +75,7 @@
 
     @Test
     @MediumTest
-    @CommandLineFlags.Add({BASE_PARAMS})
+    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/1.0"})
     public void testThumbnailAspectRatio_one() {
         int tabCounts = 11;
         TabUiTestHelper.prepareTabsWithThumbnail(mActivityTestRule, tabCounts, 0, "about:blank");
@@ -90,7 +90,7 @@
 
     @Test
     @MediumTest
-    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.85"})
+    @CommandLineFlags.Add({BASE_PARAMS})
     public void testThumbnailAspectRatio_point85() {
         int tabCounts = 11;
         TabUiTestHelper.prepareTabsWithThumbnail(mActivityTestRule, tabCounts, 0, "about:blank");
@@ -105,7 +105,7 @@
         int tabCounts = 11;
         TabUiTestHelper.prepareTabsWithThumbnail(mActivityTestRule, tabCounts, 0, "about:blank");
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
-        verifyAllThumbnailHeightWithAspectRatio(tabCounts, 1.f);
+        verifyAllThumbnailHeightWithAspectRatio(tabCounts, .85f);
 
         // With soft cleanup.
         TabUiTestHelper.leaveTabSwitcher(mActivityTestRule.getActivity());
@@ -113,7 +113,7 @@
         // There is a chance this will fail without the current changes. Soft cleanup sets the
         // fetcher to null, which triggers TabGridViewBinder#releaseThumbnail. If the view still
         // under measuring, then its height can be zero after measurement.
-        verifyAllThumbnailHeightWithAspectRatio(tabCounts, 1.f);
+        verifyAllThumbnailHeightWithAspectRatio(tabCounts, .85f);
     }
 
     private void verifyAllThumbnailHeightWithAspectRatio(int tabCounts, float ratio) {
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
index 0c1c81f1..4f4273d 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
@@ -67,7 +67,6 @@
         mPriceTabData = new PriceTabData(
                 BINDING_TAB_ID, new ShoppingPersistedTabData.PriceDrop(PRICE, PREVIOUS_PRICE));
 
-        doReturn(mPriceTabData).when(mMessageProvider).getFirstTabShowingPriceCard();
         doNothing().when(mMessageObserver).messageReady(anyInt(), any());
         doNothing().when(mMessageObserver).messageInvalidate(anyInt());
 
@@ -81,33 +80,16 @@
         mMessageService.addObserver(mMessageObserver);
     }
 
-    @Test
-    public void testPrepareMessage_messageCardDisabled() {
-        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
-                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, false);
-        mMessageService.preparePriceMessage();
-        verify(mMessageProvider, times(0)).getFirstTabShowingPriceCard();
-        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
-                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, true);
-        mMessageService.preparePriceMessage();
-        verify(mMessageProvider, times(1)).getFirstTabShowingPriceCard();
-    }
-
-    @Test
-    public void testPrepareMessage_noTabShowingPriceCard() {
-        doReturn(null).when(mMessageProvider).getFirstTabShowingPriceCard();
-        mMessageService.preparePriceMessage();
-        assertNull(mMessageService.getPriceTabDataForTesting());
-        verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
-        assertEquals(
-                INITIAL_SHOW_COUNT, PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount());
+    @Test(expected = AssertionError.class)
+    public void testPrepareMessage_nullPriceTabData() {
+        mMessageService.preparePriceMessage(null);
     }
 
     @Test
     public void testPrepareMessage_exceedMaxShowCount() {
         PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeInt(
                 PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD_SHOW_COUNT, MAX_SHOW_COUNT);
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(mPriceTabData);
         assertEquals(
                 MAX_SHOW_COUNT + 1, PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount());
         assertNull(mMessageService.getPriceTabDataForTesting());
@@ -117,9 +99,8 @@
 
     @Test
     public void testPrepareMessage_hasTabShowingPriceCard() {
-        doReturn(mPriceTabData).when(mMessageProvider).getFirstTabShowingPriceCard();
         InOrder inOrder = Mockito.inOrder(mMessageObserver);
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(mPriceTabData);
         assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
         inOrder.verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
         inOrder.verify(mMessageObserver, times(1))
@@ -128,7 +109,7 @@
 
         // We sendAvailabilityNotification only if the newly obtained priceTabData is different from
         // currently existing priceTabData.
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(mPriceTabData);
         assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
         verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
         verify(mMessageObserver, times(1))
@@ -137,8 +118,7 @@
 
         PriceTabData priceTabData = new PriceTabData(
                 BINDING_TAB_ID + 1, new ShoppingPersistedTabData.PriceDrop(PRICE, PREVIOUS_PRICE));
-        doReturn(priceTabData).when(mMessageProvider).getFirstTabShowingPriceCard();
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(priceTabData);
         assertEquals(priceTabData, mMessageService.getPriceTabDataForTesting());
         verify(mMessageObserver, times(2)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
         verify(mMessageObserver, times(2))
@@ -148,32 +128,41 @@
 
     @Test
     public void testReview() {
+        mMessageService.preparePriceMessage(mPriceTabData);
+        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
+        verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
+
         int index = 1;
         doReturn(index).when(mMessageProvider).getTabIndexFromTabId(BINDING_TAB_ID);
-        doNothing().when(mReviewActionProvider).scrollToBindingTab(anyInt());
-        mMessageService.preparePriceMessage();
+        doNothing().when(mReviewActionProvider).scrollToTab(anyInt());
         mMessageService.review();
-        verify(mReviewActionProvider).scrollToBindingTab(index);
+        verify(mReviewActionProvider).scrollToTab(index);
         verify(mMessageProvider).showPriceDropTooltip(index);
         assertTrue(PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled());
+        assertNull(mMessageService.getPriceTabDataForTesting());
     }
 
     @Test
     public void testDismiss() {
+        mMessageService.preparePriceMessage(mPriceTabData);
+        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
+        verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
+
         mMessageService.dismiss();
         assertTrue(PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled());
+        assertNull(mMessageService.getPriceTabDataForTesting());
     }
 
     @Test
     public void testGetBindingTabId() {
         assertEquals(Tab.INVALID_TAB_ID, mMessageService.getBindingTabId());
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(mPriceTabData);
         assertEquals(BINDING_TAB_ID, mMessageService.getBindingTabId());
     }
 
     @Test
     public void testInvalidateMessage() {
-        mMessageService.preparePriceMessage();
+        mMessageService.preparePriceMessage(mPriceTabData);
         assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
         verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_WELCOME));
         mMessageService.invalidateMessage();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 3666ec0..9fb04568 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -120,7 +120,9 @@
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
+import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.ShoppingPersistedTabDataFetcher;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.util.browser.Features;
@@ -251,6 +253,10 @@
     TabListMediator.TabGridAccessibilityHelper mTabGridAccessibilityHelper;
     @Mock
     TemplateUrlService mTemplateUrlService;
+    @Mock
+    TabSwitcherMediator.PriceWelcomeMessageController mPriceWelcomeMessageController;
+    @Mock
+    ShoppingPersistedTabData mShoppingPersistedTabData;
 
     @Captor
     ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
@@ -279,6 +285,8 @@
     private View mItemView2 = mock(View.class);
     private TabModelObserver mMediatorTabModelObserver;
     private TabGroupModelFilter.Observer mMediatorTabGroupModelFilterObserver;
+    private PriceDrop mPriceDrop;
+    private PriceTabData mPriceTabData;
 
     @Before
     public void setUp() {
@@ -364,7 +372,7 @@
         TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, false, null, mGridCardOnClickListenerProvider, null,
+                mTabListFaviconProvider, false, null, mGridCardOnClickListenerProvider, null, null,
                 getClass().getSimpleName(), UiType.CLOSABLE);
         mMediator.registerOrientationListener(mGridLayoutManager);
         TrackerFactory.setTrackerForTests(mTracker);
@@ -1575,23 +1583,6 @@
     }
 
     @Test
-    public void testGetFirstTabShowingPriceCard() {
-        initAndAssertAllProperties();
-        mModel.get(0).model.set(TabProperties.PRICE_DROP, null);
-        mModel.get(1).model.set(TabProperties.PRICE_DROP, null);
-        assertNull(mModel.getFirstTabShowingPriceCard());
-
-        PriceDrop priceDrop1 = new PriceDrop("$1", "$2");
-        PriceDrop priceDrop2 = new PriceDrop("$3", "$4");
-        mModel.get(1).model.set(TabProperties.PRICE_DROP, priceDrop2);
-        assertEquals(TAB2_ID, mModel.getFirstTabShowingPriceCard().bindingTabId);
-        assertEquals(priceDrop2, mModel.getFirstTabShowingPriceCard().priceDrop);
-        mModel.get(0).model.set(TabProperties.PRICE_DROP, priceDrop1);
-        assertEquals(TAB1_ID, mModel.getFirstTabShowingPriceCard().bindingTabId);
-        assertEquals(priceDrop1, mModel.getFirstTabShowingPriceCard().priceDrop);
-    }
-
-    @Test
     @Features.DisableFeatures({TAB_GROUPS_ANDROID})
     public void testUrlUpdated_forSingleTab_GTS_GroupNotEnabled() {
         initAndAssertAllProperties();
@@ -1914,6 +1905,7 @@
         assertThat(mModel.get(0).model.get(TabProperties.SEARCH_QUERY), equalTo(searchTerm1));
     }
 
+    // TODO(crbug.com/1177036): the assertThat in fetch callback is never reached.
     @Test
     public void testPriceTrackingProperty() {
         TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
@@ -2169,7 +2161,7 @@
         // Re-initialize the mediator to setup TemplateUrlServiceObserver if needed.
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                mTabListFaviconProvider, true, null, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
         mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
@@ -2194,7 +2186,7 @@
         // Re-initialize the mediator to setup TemplateUrlServiceObserver if needed.
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                mTabListFaviconProvider, true, null, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
         mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
@@ -2333,33 +2325,11 @@
     }
 
     @Test
-    public void testGetIndexForSelectedTab() {
-        initAndAssertAllProperties();
-        assertThat(mModel.getIndexForSelectedTab(), equalTo(0));
-    }
-
-    @Test(expected = AssertionError.class)
-    public void testGetIndexForSelectedTab_multipleSelectedTabs() {
-        initAndAssertAllProperties();
-        mModel.get(0).model.set(TabProperties.IS_SELECTED, true);
-        mModel.get(1).model.set(TabProperties.IS_SELECTED, true);
-        mModel.getIndexForSelectedTab();
-    }
-
-    @Test(expected = AssertionError.class)
-    public void testGetIndexForSelectedTab_noSelectedTabs() {
-        initAndAssertAllProperties();
-        mModel.get(0).model.set(TabProperties.IS_SELECTED, false);
-        mModel.get(1).model.set(TabProperties.IS_SELECTED, false);
-        mModel.getIndexForSelectedTab();
-    }
-
-    @Test
     public void testListObserver_OnItemRangeInserted() {
         TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                mTabListFaviconProvider, true, null, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
         mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
@@ -2377,7 +2347,7 @@
         TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                mTabListFaviconProvider, true, null, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
         mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
@@ -2393,6 +2363,68 @@
     }
 
     @Test
+    public void testMaybeShowPriceWelcomeMessage() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+        ShoppingPersistedTabDataFetcher fetcher =
+                new ShoppingPersistedTabDataFetcher(mTab1, mModel, mPriceWelcomeMessageController);
+        fetcher.maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(1)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
+    public void testMaybeShowPriceWelcomeMessage_MessageDisabled() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+        ShoppingPersistedTabDataFetcher fetcher =
+                new ShoppingPersistedTabDataFetcher(mTab1, mModel, mPriceWelcomeMessageController);
+
+        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
+                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, false);
+        assertThat(PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled(), equalTo(true));
+        fetcher.maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
+    public void testMaybeShowPriceWelcomeMessage_NullParameter() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+
+        new ShoppingPersistedTabDataFetcher(mTab1, null, mPriceWelcomeMessageController)
+                .maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+
+        new ShoppingPersistedTabDataFetcher(mTab1, mModel, null)
+                .maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
+    public void testMaybeShowPriceWelcomeMessage_NoPriceDrop() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+        ShoppingPersistedTabDataFetcher fetcher =
+                new ShoppingPersistedTabDataFetcher(mTab1, mModel, mPriceWelcomeMessageController);
+
+        fetcher.maybeShowPriceWelcomeMessage(null);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+
+        doReturn(null).when(mShoppingPersistedTabData).getPriceDrop();
+        fetcher.maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
+    public void testMaybeShowPriceWelcomeMessage_AlreadyHasMessage() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+        ShoppingPersistedTabDataFetcher fetcher =
+                new ShoppingPersistedTabDataFetcher(mTab1, mModel, mPriceWelcomeMessageController);
+
+        // Simulate that we already has the message.
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+
+        fetcher.maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
     @Features.EnableFeatures({TAB_GROUPS_CONTINUATION_ANDROID})
     public void testUpdateFaviconForGroup() {
         setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
@@ -2731,7 +2763,7 @@
 
         mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
-                mTabListFaviconProvider, actionOnRelatedTabs, null, null, handler,
+                mTabListFaviconProvider, actionOnRelatedTabs, null, null, handler, null,
                 getClass().getSimpleName(), uiType);
         mMediator.registerOrientationListener(mGridLayoutManager);
 
@@ -2811,4 +2843,15 @@
         doReturn(1).when(mSpanSizeLookup).getSpanSize(anyInt());
         mMediator.addSpecialItemToModel(index, uiType, model);
     }
+
+    private void prepareTestMaybeShowPriceWelcomeMessage() {
+        initAndAssertAllProperties();
+        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
+                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, true);
+        mPriceDrop = new PriceDrop("1", "2");
+        mPriceTabData = new PriceTabData(TAB1_ID, mPriceDrop);
+        doReturn(mPriceDrop).when(mShoppingPersistedTabData).getPriceDrop();
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME),
+                equalTo(TabModel.INVALID_TAB_INDEX));
+    }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index fe23957..3935675 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -587,16 +587,11 @@
         doReturn(1).when(mTabModel).getCount();
         doReturn(TAB1_ID).when(mPriceWelcomeMessageService).getBindingTabId();
         mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mPriceWelcomeMessageController, times(0)).restorePriceWelcomeMessage();
+        verify(mPriceWelcomeMessageController, times(1)).restorePriceWelcomeMessage();
 
         doReturn(2).when(mTabModel).getCount();
         doReturn(TAB2_ID).when(mPriceWelcomeMessageService).getBindingTabId();
         mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mPriceWelcomeMessageController, times(0)).restorePriceWelcomeMessage();
-
-        doReturn(2).when(mTabModel).getCount();
-        doReturn(TAB1_ID).when(mPriceWelcomeMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
         verify(mPriceWelcomeMessageController, times(1)).restorePriceWelcomeMessage();
     }
 
@@ -614,6 +609,15 @@
     }
 
     @Test
+    public void testScrollToTab() {
+        initAndAssertAllProperties();
+        mMediator.scrollToTab(0);
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+        mMediator.scrollToTab(1);
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(1));
+    }
+
+    @Test
     public void showOverviewDoesNotUpdateResetHandlerBeforeRestoreCompleted() {
         initAndAssertAllProperties();
         doReturn(false).when(mTabModelSelector).isTabStateInitialized();
diff --git a/chrome/android/feed/README.md b/chrome/android/feed/README.md
deleted file mode 100644
index 9f47f17..0000000
--- a/chrome/android/feed/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Feed Host UI
-
-The feed Java package provides support for showing a list of article
-suggestions rendered by the third party
-[Feed](https://chromium.googlesource.com/feed) library in Chrome UI.
-
-This directory contains two mirrored packages that provide real and dummy
-implementations of classes to facilitate compiling out dependencies on
-[//third_party/feed_library/](../../../third_party/feed_library/) and
-[//components/feed/](../../../components/feed/) when the `enable_feed_in_chrome`
-build flag is disabled. The public classes and methods in real/dummy used by
-[//chrome/android/java/](../java/) must have identical signatures.
\ No newline at end of file
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/knowncontent/ContentMetadataTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/knowncontent/ContentMetadataTest.java
deleted file mode 100644
index 165f064..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/knowncontent/ContentMetadataTest.java
+++ /dev/null
@@ -1,85 +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.
-
-package org.chromium.chrome.browser.feed.library.api.client.knowncontent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.OfflineMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.RepresentationData;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ContentMetadata}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentMetadataTest {
-    private static final String TITLE = "title";
-    private static final String SNIPPET = "snippet";
-    private static final String FAVICON_URL = "favicon.com";
-    private static final String IMAGE_URL = "image.com";
-    private static final String PUBLISHER = "publisher";
-    private static final String URL = "url.com";
-    private static final long TIME_PUBLISHED = 6L;
-
-    private static final RepresentationData REPRESENTATION_DATA =
-            RepresentationData.newBuilder()
-                    .setUri(URL)
-                    .setPublishedTimeSeconds(TIME_PUBLISHED)
-                    .build();
-    private static final RepresentationData REPRESENTATION_DATA_NO_URL =
-            REPRESENTATION_DATA.toBuilder().clearUri().build();
-    private static final RepresentationData REPRESENTATION_DATA_NO_TIME_PUBLISHED =
-            REPRESENTATION_DATA.toBuilder().clearPublishedTimeSeconds().build();
-
-    private static final OfflineMetadata OFFLINE_METADATA = OfflineMetadata.newBuilder()
-                                                                    .setFaviconUrl(FAVICON_URL)
-                                                                    .setImageUrl(IMAGE_URL)
-                                                                    .setPublisher(PUBLISHER)
-                                                                    .setSnippet(SNIPPET)
-                                                                    .setTitle(TITLE)
-                                                                    .build();
-    private static final OfflineMetadata OFFLINE_METADATA_NO_TITLE =
-            OFFLINE_METADATA.toBuilder().clearTitle().build();
-
-    @Test
-    public void testMaybeCreate() {
-        ContentMetadata created =
-                ContentMetadata.maybeCreateContentMetadata(OFFLINE_METADATA, REPRESENTATION_DATA);
-
-        assertThat(created.getTitle()).isEqualTo(TITLE);
-        assertThat(created.getSnippet()).isEqualTo(SNIPPET);
-        assertThat(created.getFaviconUrl()).isEqualTo(FAVICON_URL);
-        assertThat(created.getImageUrl()).isEqualTo(IMAGE_URL);
-        assertThat(created.getPublisher()).isEqualTo(PUBLISHER);
-        assertThat(created.getUrl()).isEqualTo(URL);
-        assertThat(created.getTimePublished()).isEqualTo(TIME_PUBLISHED);
-    }
-
-    @Test
-    public void testMaybeCreate_noUrl() {
-        assertThat(ContentMetadata.maybeCreateContentMetadata(
-                           OFFLINE_METADATA, REPRESENTATION_DATA_NO_URL))
-                .isNull();
-    }
-
-    @Test
-    public void testMaybeCreate_noTitle() {
-        assertThat(ContentMetadata.maybeCreateContentMetadata(
-                           OFFLINE_METADATA_NO_TITLE, REPRESENTATION_DATA))
-                .isNull();
-    }
-
-    @Test
-    public void testMaybeCreate_noTimePublished() {
-        assertThat(ContentMetadata
-                           .maybeCreateContentMetadata(
-                                   OFFLINE_METADATA, REPRESENTATION_DATA_NO_TIME_PUBLISHED)
-                           .getTimePublished())
-                .isEqualTo(ContentMetadata.UNKNOWN_TIME_PUBLISHED);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/ProcessScopeBuilderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/ProcessScopeBuilderTest.java
deleted file mode 100644
index 8544aa9fe..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/ProcessScopeBuilderTest.java
+++ /dev/null
@@ -1,192 +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.
-
-package org.chromium.chrome.browser.feed.library.api.client.scope;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.network.NetworkClient;
-import org.chromium.chrome.browser.feed.library.api.host.proto.ProtoExtensionProvider;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorage;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalStorage;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipSupportedApi;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProviderJni;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-
-/** Tests for {@link ProcessScopeBuilder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-@Features.DisableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-public class ProcessScopeBuilderTest {
-    // Mocks for required fields
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private NetworkClient mNetworkClient;
-    @Mock
-    private SchedulerApi mSchedulerApi;
-    @Mock
-    private ApplicationInfo mApplicationInfo;
-    @Mock
-    private TooltipSupportedApi mTooltipSupportedApi;
-    @Mock
-    private IdentityServicesProvider.Natives mIdentityServicesProviderJniMock;
-    @Mock
-    private Profile mProfileMock;
-    @Mock
-    private IdentityManager mIdentifiyManagerMock;
-
-    // Mocks for optional fields
-    @Mock
-    private ThreadUtils mThreadUtils;
-
-    private final ProtoExtensionProvider mProtoExtensionProvider = ArrayList::new;
-    private Configuration mConfiguration = new Configuration.Builder().build();
-    private Context mContext;
-
-    @Rule
-    public JniMocker jniMocker = new JniMocker();
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        Profile.setLastUsedProfileForTesting(mProfileMock);
-        jniMocker.mock(IdentityServicesProviderJni.TEST_HOOKS, mIdentityServicesProviderJniMock);
-        when(mIdentityServicesProviderJniMock.getIdentityManager(mProfileMock))
-                .thenReturn(mIdentifiyManagerMock);
-    }
-
-    @After
-    public void tearDown() {
-        Profile.setLastUsedProfileForTesting(null);
-    }
-
-    @Test
-    public void testBasicBuild() {
-        // No crash should happen.
-        ProcessScope processScope =
-                new ProcessScopeBuilder(mConfiguration, MoreExecutors.newDirectExecutorService(),
-                        mBasicLoggingApi, mNetworkClient, mSchedulerApi, DebugBehavior.VERBOSE,
-                        mContext, mApplicationInfo, mTooltipSupportedApi)
-                        .setJournalStorageDirect(mock(JournalStorageDirect.class))
-                        .setContentStorageDirect(mock(ContentStorageDirect.class))
-                        .build();
-
-        assertThat(processScope.getRequestManager()).isNotNull();
-        assertThat(processScope.getAppLifecycleListener()).isNotNull();
-        assertThat(processScope.getKnownContent()).isNotNull();
-        assertThat(processScope.getAppLifecycleListener()).isNotNull();
-    }
-
-    @Test
-    public void testComplexBuild() {
-        // No crash should happen.
-        ProcessScope processScope =
-                new ProcessScopeBuilder(mConfiguration, MoreExecutors.newDirectExecutorService(),
-                        mBasicLoggingApi, mNetworkClient, mSchedulerApi, DebugBehavior.VERBOSE,
-                        mContext, mApplicationInfo, mTooltipSupportedApi)
-                        .setProtoExtensionProvider(mProtoExtensionProvider)
-                        .setJournalStorageDirect(mock(JournalStorageDirect.class))
-                        .setContentStorageDirect(mock(ContentStorageDirect.class))
-                        .build();
-
-        assertThat(processScope.getRequestManager()).isNotNull();
-        assertThat(processScope.getAppLifecycleListener()).isNotNull();
-        assertThat(processScope.getAppLifecycleListener()).isNotNull();
-    }
-
-    @Test
-    public void testDirectStorage() {
-        ContentStorageDirect contentStorageDirect = mock(ContentStorageDirect.class);
-        JournalStorageDirect journalStorageDirect = mock(JournalStorageDirect.class);
-        ProcessScopeBuilder builder =
-                new ProcessScopeBuilder(mConfiguration, MoreExecutors.newDirectExecutorService(),
-                        mBasicLoggingApi, mNetworkClient, mSchedulerApi, DebugBehavior.VERBOSE,
-                        mContext, mApplicationInfo, mTooltipSupportedApi)
-                        .setContentStorageDirect(contentStorageDirect)
-                        .setJournalStorageDirect(journalStorageDirect);
-        assertThat(builder.mContentStorage).isEqualTo(contentStorageDirect);
-        assertThat(builder.mJournalStorage).isEqualTo(journalStorageDirect);
-    }
-
-    @Test
-    public void testStorage_direct() {
-        mConfiguration =
-                new Configuration.Builder().put(ConfigKey.USE_DIRECT_STORAGE, true).build();
-        ContentStorage contentStorageDirect = mock(
-                ContentStorage.class, withSettings().extraInterfaces(ContentStorageDirect.class));
-        JournalStorage journalStorageDirect = mock(
-                JournalStorage.class, withSettings().extraInterfaces(JournalStorageDirect.class));
-        ProcessScopeBuilder builder =
-                new ProcessScopeBuilder(mConfiguration, MoreExecutors.newDirectExecutorService(),
-                        mBasicLoggingApi, mNetworkClient, mSchedulerApi, DebugBehavior.VERBOSE,
-                        mContext, mApplicationInfo, mTooltipSupportedApi)
-                        .setContentStorage(contentStorageDirect)
-                        .setJournalStorage(journalStorageDirect);
-        MainThreadRunner mainThreadRunner = new MainThreadRunner();
-        assertThat(builder.buildContentStorage(mainThreadRunner)).isEqualTo(contentStorageDirect);
-        assertThat(builder.buildJournalStorage(mainThreadRunner)).isEqualTo(journalStorageDirect);
-    }
-
-    @Test
-    public void testStorage_wrapped() {
-        mConfiguration =
-                new Configuration.Builder().put(ConfigKey.USE_DIRECT_STORAGE, false).build();
-        ContentStorage contentStorageDirect = mock(
-                ContentStorage.class, withSettings().extraInterfaces(ContentStorageDirect.class));
-        JournalStorage journalStorageDirect = mock(
-                JournalStorage.class, withSettings().extraInterfaces(JournalStorageDirect.class));
-        ProcessScopeBuilder builder =
-                new ProcessScopeBuilder(mConfiguration, MoreExecutors.newDirectExecutorService(),
-                        mBasicLoggingApi, mNetworkClient, mSchedulerApi, DebugBehavior.VERBOSE,
-                        mContext, mApplicationInfo, mTooltipSupportedApi)
-                        .setContentStorage(contentStorageDirect)
-                        .setJournalStorage(journalStorageDirect);
-        assertThat(builder.mContentStorage).isNotEqualTo(contentStorageDirect);
-        assertThat(builder.mJournalStorage).isNotEqualTo(journalStorageDirect);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/StreamScopeBuilderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/StreamScopeBuilderTest.java
deleted file mode 100644
index 416a627ab..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/client/scope/StreamScopeBuilderTest.java
+++ /dev/null
@@ -1,185 +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.
-
-package org.chromium.chrome.browser.feed.library.api.client.scope;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo.BuildType;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.imageloader.ImageLoaderApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.offlineindicator.OfflineIndicatorApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.SnackbarApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.StreamConfiguration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipSupportedApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.knowncontent.FeedKnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.stream.StreamFactory;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.chrome.browser.feed.shared.stream.Stream;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link StreamScopeBuilder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamScopeBuilderTest {
-    @BuildType
-    private static final int BUILD_TYPE = BuildType.ALPHA;
-    public static final DebugBehavior DEBUG_BEHAVIOR = DebugBehavior.VERBOSE;
-
-    // Mocks for required fields
-    @Mock
-    private ActionApi mActionApi;
-    @Mock
-    private ImageLoaderApi mImageLoaderApi;
-    @Mock
-    private TooltipSupportedApi mTooltipSupportedApi;
-
-    // Mocks for optional fields
-    @Mock
-    private ProtocolAdapter mProtocolAdapter;
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private Stream mStream;
-    @Mock
-    private StreamConfiguration mStreamConfiguration;
-    @Mock
-    private CardConfiguration mCardConfiguration;
-    @Mock
-    private ModelProviderFactory mModelProviderFactory;
-    @Mock
-    private CustomElementProvider mCustomElementProvider;
-    @Mock
-    private HostBindingProvider mHostBindingProvider;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private Configuration mConfig;
-    @Mock
-    private SnackbarApi mSnackbarApi;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private OfflineIndicatorApi mOfflineIndicatorApi;
-    @Mock
-    private FeedKnownContent mFeedKnownContent;
-    @Mock
-    private TaskQueue mTaskQueue;
-    @Mock
-    private TooltipApi mTooltipApi;
-    @Mock
-    private FeedExtensionRegistry mFeedExtensionRegistry;
-
-    private Activity mActivity;
-    private MainThreadRunner mMainThreadRunner;
-    private ThreadUtils mThreadUtils;
-    private TimingUtils mTimingUtils;
-    private Clock mClock;
-    private ApplicationInfo mApplicationInfo;
-    private StreamFactory mStreamFactory;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mActivity = Robolectric.buildActivity(Activity.class).get();
-        mMainThreadRunner = new MainThreadRunner();
-        mThreadUtils = new ThreadUtils();
-        mTimingUtils = new TimingUtils();
-        mClock = new FakeClock();
-        mApplicationInfo = new ApplicationInfo.Builder(mActivity).setBuildType(BUILD_TYPE).build();
-        when(mConfig.getValueOrDefault(
-                     eq(ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS), anyLong()))
-                .thenReturn(1000L);
-        when(mConfig.getValueOrDefault(eq(ConfigKey.FADE_IMAGE_THRESHOLD_MS), anyLong()))
-                .thenReturn(80L);
-    }
-
-    @Test
-    public void testBasicBuild() {
-        StreamScope streamScope = new StreamScopeBuilder(mActivity, mActionApi, mImageLoaderApi,
-                mProtocolAdapter, mFeedSessionManager, mThreadUtils, mTimingUtils, mTaskQueue,
-                mMainThreadRunner, mClock, DEBUG_BEHAVIOR, mStreamConfiguration, mCardConfiguration,
-                mActionManager, mConfig, mSnackbarApi, mBasicLoggingApi, mOfflineIndicatorApi,
-                mFeedKnownContent, mTooltipApi, mTooltipSupportedApi, mApplicationInfo,
-                mFeedExtensionRegistry)
-                                          .build();
-        assertThat(streamScope.getStream()).isNotNull();
-        assertThat(streamScope.getModelProviderFactory()).isNotNull();
-    }
-
-    @Test
-    public void testComplexBuild() {
-        StreamScope streamScope = new StreamScopeBuilder(mActivity, mActionApi, mImageLoaderApi,
-                mProtocolAdapter, mFeedSessionManager, mThreadUtils, mTimingUtils, mTaskQueue,
-                mMainThreadRunner, mClock, DEBUG_BEHAVIOR, mStreamConfiguration, mCardConfiguration,
-                mActionManager, mConfig, mSnackbarApi, mBasicLoggingApi, mOfflineIndicatorApi,
-                mFeedKnownContent, mTooltipApi, mTooltipSupportedApi, mApplicationInfo,
-                mFeedExtensionRegistry)
-                                          .setModelProviderFactory(mModelProviderFactory)
-                                          .setCustomElementProvider(mCustomElementProvider)
-                                          .setHostBindingProvider(new HostBindingProvider())
-                                          .build();
-        assertThat(streamScope.getStream()).isNotNull();
-        assertThat(streamScope.getModelProviderFactory()).isEqualTo(mModelProviderFactory);
-    }
-
-    @Test
-    public void testStreamFactoryBuild() {
-        setupStreamFactory(mStream);
-
-        StreamScope streamScope = new StreamScopeBuilder(mActivity, mActionApi, mImageLoaderApi,
-                mProtocolAdapter, mFeedSessionManager, mThreadUtils, mTimingUtils, mTaskQueue,
-                mMainThreadRunner, mClock, DEBUG_BEHAVIOR, mStreamConfiguration, mCardConfiguration,
-                mActionManager, mConfig, mSnackbarApi, mBasicLoggingApi, mOfflineIndicatorApi,
-                mFeedKnownContent, mTooltipApi, mTooltipSupportedApi, mApplicationInfo,
-                mFeedExtensionRegistry)
-                                          .setStreamFactory(mStreamFactory)
-                                          .setCustomElementProvider(mCustomElementProvider)
-                                          .setHostBindingProvider(mHostBindingProvider)
-                                          .build();
-        assertThat(streamScope.getStream()).isEqualTo(mStream);
-    }
-
-    private void setupStreamFactory(Stream streamToReturn) {
-        mStreamFactory = (actionParserFactory, context, buildType, cardConfiguration,
-                imageLoaderApi, customElementProvider, debugBehavior, clock, modelProviderFactory,
-                hostBindingProvider, offlineIndicatorApi, configuration, actionApi, actionManager,
-                snackbarApi, streamConfiguration, feedExtensionRegistry, basicLoggingApi,
-                mainThreadRunner, isBackgroundDark, tooltipApi, threadUtils, knownContentApi,
-                isPlaceholderShown) -> streamToReturn;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/common/MutationContextTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/common/MutationContextTest.java
deleted file mode 100644
index c88ce0e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/common/MutationContextTest.java
+++ /dev/null
@@ -1,45 +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.
-
-package org.chromium.chrome.browser.feed.library.api.common;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-
-/** Tests of the {@link MutationContext} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class MutationContextTest {
-    @Test
-    public void testBuilder() {
-        ByteString tokenBytes = ByteString.copyFrom("token", Charset.defaultCharset());
-        StreamToken token = StreamToken.newBuilder().setNextPageToken(tokenBytes).build();
-        String sessionId = "session:1";
-        MutationContext mutationContext = new MutationContext.Builder()
-                                                  .setContinuationToken(token)
-                                                  .setRequestingSessionId(sessionId)
-                                                  .build();
-        assertThat(mutationContext).isNotNull();
-        assertThat(mutationContext.getContinuationToken()).isEqualTo(token);
-        assertThat(mutationContext.getRequestingSessionId()).isEqualTo(sessionId);
-        assertThat(mutationContext.isUserInitiated()).isFalse();
-    }
-
-    @Test
-    public void testUserInitiated_true() {
-        MutationContext mutationContext =
-                new MutationContext.Builder().setUserInitiated(true).build();
-        assertThat(mutationContext.isUserInitiated()).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/config/ConfigurationTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/config/ConfigurationTest.java
deleted file mode 100644
index 07007d9..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/config/ConfigurationTest.java
+++ /dev/null
@@ -1,94 +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.
-
-package org.chromium.chrome.browser.feed.library.api.host.config;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.fail;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link Configuration}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ConfigurationTest {
-    @ConfigKey
-    private static final String CONFIG_KEY_WITH_DEFAULT = "config-test";
-
-    private static final boolean CONFIG_KEY_WITH_DEFAULT_VALUE = false;
-
-    @Test
-    public void defaultConfig_hasDefaultValues() {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(CONFIG_KEY_WITH_DEFAULT, CONFIG_KEY_WITH_DEFAULT_VALUE)
-                        .build();
-
-        boolean valueOrDefault = configuration.getValueOrDefault(
-                CONFIG_KEY_WITH_DEFAULT, !CONFIG_KEY_WITH_DEFAULT_VALUE);
-
-        assertThat(valueOrDefault).isEqualTo(CONFIG_KEY_WITH_DEFAULT_VALUE);
-    }
-
-    @Test
-    public void hasValue_forValueThatDoesNotExist_returnsFalse() {
-        Configuration configuration = new Configuration.Builder().build();
-
-        assertThat(configuration.hasValue(ConfigKey.FEED_SERVER_ENDPOINT)).isFalse();
-    }
-
-    @Test
-    public void getValueOrDefault_forValueThatDoesNotExist_returnsSpecifiedDefault() {
-        final String defaultString = "defaultString";
-        Configuration configuration = new Configuration.Builder().build();
-
-        assertThat(configuration.getValueOrDefault(ConfigKey.FEED_SERVER_ENDPOINT, defaultString))
-                .isEqualTo(defaultString);
-    }
-
-    @Test
-    public void getValueOrDefault_forValueThatExists_returnsValue() {
-        final String someValue = "someValue";
-        Configuration configuration =
-                new Configuration.Builder().put(ConfigKey.FEED_SERVER_ENDPOINT, someValue).build();
-
-        assertThat(configuration.getValueOrDefault(ConfigKey.FEED_SERVER_ENDPOINT, ""))
-                .isEqualTo(someValue);
-    }
-
-    @Test
-    public void getValueOrDefaultWithWrongType_throwsClassCastException() {
-        Configuration configuration = new Configuration.Builder()
-                                              .put(ConfigKey.FEED_SERVER_ENDPOINT, "someString")
-                                              .build();
-
-        try {
-            @SuppressWarnings("unused") // Used for type inference
-            Boolean ignored =
-                    configuration.getValueOrDefault(ConfigKey.FEED_SERVER_ENDPOINT, false);
-            fail();
-        } catch (ClassCastException ignored) {
-            // expected
-        }
-    }
-
-    @Test
-    public void getValue_forValueThatWasOverridden_ReturnsOverriddenValue() {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(CONFIG_KEY_WITH_DEFAULT, !CONFIG_KEY_WITH_DEFAULT_VALUE)
-                        .build();
-
-        boolean valueOrDefault = configuration.getValueOrDefault(
-                CONFIG_KEY_WITH_DEFAULT, !CONFIG_KEY_WITH_DEFAULT_VALUE);
-
-        assertThat(valueOrDefault).isEqualTo(!CONFIG_KEY_WITH_DEFAULT_VALUE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpHeaderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpHeaderTest.java
deleted file mode 100644
index a41dcda..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpHeaderTest.java
+++ /dev/null
@@ -1,30 +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.
-
-package org.chromium.chrome.browser.feed.library.api.host.network;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpHeader.HttpHeaderName;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test class for {@link HttpHeader} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HttpHeaderTest {
-    private static final String HEADER_VALUE = "Hello world";
-
-    @Test
-    public void testConstructor() {
-        HttpHeader header =
-                new HttpHeader(HttpHeaderName.X_PROTOBUFFER_REQUEST_PAYLOAD, HEADER_VALUE);
-
-        assertThat(header.getName()).isEqualTo(HttpHeaderName.X_PROTOBUFFER_REQUEST_PAYLOAD);
-        assertThat(header.getValue()).isEqualTo(HEADER_VALUE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpRequestTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpRequestTest.java
deleted file mode 100644
index 17ffa3a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/network/HttpRequestTest.java
+++ /dev/null
@@ -1,97 +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.
-
-package org.chromium.chrome.browser.feed.library.api.host.network;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.net.Uri;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpHeader.HttpHeaderName;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest.HttpMethod;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Test class for {@link HttpRequest} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HttpRequestTest {
-    public static final String HELLO_WORLD = "hello world";
-
-    @Test
-    public void testConstructor() throws Exception {
-        Uri uri = Uri.EMPTY;
-        String httpMethod = HttpMethod.GET;
-        HttpRequest httpRequest =
-                new HttpRequest(uri, httpMethod, Collections.emptyList(), new byte[] {});
-
-        assertThat(httpRequest.getUri()).isEqualTo(uri);
-        assertThat(httpRequest.getMethod()).isEqualTo(httpMethod);
-    }
-
-    @Test
-    public void testConstructor_withHeaders() throws Exception {
-        Uri uri = Uri.EMPTY;
-        String httpMethod = HttpMethod.POST;
-        List<HttpHeader> headers = Collections.singletonList(
-                new HttpHeader(HttpHeaderName.X_PROTOBUFFER_REQUEST_PAYLOAD, HELLO_WORLD));
-        HttpRequest httpRequest = new HttpRequest(uri, httpMethod, headers, new byte[] {});
-
-        assertThat(httpRequest.getUri()).isEqualTo(uri);
-        assertThat(httpRequest.getMethod()).isEqualTo(httpMethod);
-        assertThat(httpRequest.getHeaders()).isEqualTo(headers);
-    }
-
-    @Test
-    public void testConstructor_withBody() throws Exception {
-        Uri uri = Uri.EMPTY;
-        byte[] bytes = new byte[] {};
-        String httpMethod = HttpMethod.POST;
-        HttpRequest httpRequest = new HttpRequest(uri, httpMethod, Collections.emptyList(), bytes);
-
-        assertThat(httpRequest.getUri()).isEqualTo(uri);
-        assertThat(httpRequest.getBody()).isEqualTo(bytes);
-        assertThat(httpRequest.getMethod()).isEqualTo(httpMethod);
-    }
-
-    @Test
-    public void testConstructor_withHeadersAndBody() throws Exception {
-        Uri uri = Uri.EMPTY;
-        byte[] bytes = new byte[] {};
-        String httpMethod = HttpMethod.POST;
-        List<HttpHeader> headers = Collections.singletonList(
-                new HttpHeader(HttpHeaderName.X_PROTOBUFFER_REQUEST_PAYLOAD, HELLO_WORLD));
-        HttpRequest httpRequest = new HttpRequest(uri, httpMethod, headers, bytes);
-
-        assertThat(httpRequest.getUri()).isEqualTo(uri);
-        assertThat(httpRequest.getBody()).isEqualTo(bytes);
-        assertThat(httpRequest.getMethod()).isEqualTo(httpMethod);
-        assertThat(httpRequest.getHeaders()).isEqualTo(headers);
-    }
-
-    @Test
-    public void immutableHeaders() {
-        Uri uri = Uri.EMPTY;
-        byte[] bytes = new byte[] {};
-        String httpMethod = HttpMethod.POST;
-        HttpHeader header =
-                new HttpHeader(HttpHeaderName.X_PROTOBUFFER_REQUEST_PAYLOAD, HELLO_WORLD);
-        List<HttpHeader> headers = Collections.singletonList(header);
-        HttpRequest httpRequest = new HttpRequest(uri, httpMethod, headers, bytes);
-
-        List<HttpHeader> requestHeaders = httpRequest.getHeaders();
-        assertThatRunnable(() -> requestHeaders.add(new HttpHeader("test", "testValue")))
-                .throwsAnExceptionOfType(UnsupportedOperationException.class);
-        assertThatRunnable(() -> requestHeaders.remove(header))
-                .throwsAnExceptionOfType(UnsupportedOperationException.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/storage/CommitResultTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/storage/CommitResultTest.java
deleted file mode 100644
index 8db01286..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/host/storage/CommitResultTest.java
+++ /dev/null
@@ -1,41 +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.
-
-package org.chromium.chrome.browser.feed.library.api.host.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.storage.CommitResult.Result;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test class for {@link CommitResult} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CommitResultTest {
-    @Test
-    public void testSuccess() {
-        assertThat(CommitResult.SUCCESS.getResult()).isEqualTo(Result.SUCCESS);
-    }
-
-    @Test
-    public void testSuccessSingleton() {
-        CommitResult success = CommitResult.SUCCESS;
-        assertThat(success).isEqualTo(CommitResult.SUCCESS);
-    }
-
-    @Test
-    public void testFailure() {
-        assertThat(CommitResult.FAILURE.getResult()).isEqualTo(Result.FAILURE);
-    }
-
-    @Test
-    public void testFailureSingleton() {
-        CommitResult failure = CommitResult.FAILURE;
-        assertThat(failure).isEqualTo(CommitResult.FAILURE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/common/ThreadUtilsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/common/ThreadUtilsTest.java
deleted file mode 100644
index d5804da..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/common/ThreadUtilsTest.java
+++ /dev/null
@@ -1,43 +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.
-
-package org.chromium.chrome.browser.feed.library.api.internal.common;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests of the {@link ThreadUtils} class. Robolectric runs everything on the main thread, so the
- * tests are written to test the checks properly.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ThreadUtilsTest {
-    @Test
-    public void testOnMainThread() {
-        ThreadUtils threadUtils = new ThreadUtils();
-        assertThat(threadUtils.isMainThread()).isTrue();
-    }
-
-    @Test
-    public void testCheckMainThread() {
-        ThreadUtils threadUtils = new ThreadUtils();
-        // expect no exception
-        threadUtils.checkMainThread();
-    }
-
-    @Test()
-    public void testCheckNotMainThread() {
-        final ThreadUtils threadUtils = new ThreadUtils();
-        assertThatRunnable(threadUtils::checkNotMainThread)
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/RemoveTrackingTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/RemoveTrackingTest.java
deleted file mode 100644
index 338c12e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/RemoveTrackingTest.java
+++ /dev/null
@@ -1,86 +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.
-
-package org.chromium.chrome.browser.feed.library.api.internal.modelprovider;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.Function;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests of the {@link RemoveTracking} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RemoveTrackingTest {
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final String mRootContentId = mIdGenerators.createRootContentId(0);
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-    }
-
-    @Test
-    public void testEmpty() {
-        RemoveTracking<String> removeTracking = getRemoveTracking(
-                this::simpleTransform, (contentIds) -> assertThat(contentIds).hasSize(0));
-        removeTracking.triggerConsumerUpdate();
-    }
-
-    @Test
-    public void testMatch() {
-        RemoveTracking<String> removeTracking = getRemoveTracking(
-                this::simpleTransform, (contentIds) -> assertThat(contentIds).hasSize(1));
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addFeature(mIdGenerators.createFeatureContentId(0), mRootContentId);
-        List<PayloadWithId> payloads = protocolBuilder.buildAsPayloadWithId();
-        for (PayloadWithId payload : payloads) {
-            assertThat(payload.payload.hasStreamFeature()).isTrue();
-            removeTracking.filterStreamFeature(payload.payload.getStreamFeature());
-        }
-        removeTracking.triggerConsumerUpdate();
-    }
-
-    @Test
-    public void testNoMatch() {
-        RemoveTracking<String> removeTracking = getRemoveTracking(
-                this::nullTransform, (contentIds) -> assertThat(contentIds).hasSize(0));
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addFeature(mIdGenerators.createFeatureContentId(0), mRootContentId);
-        List<PayloadWithId> payloads = protocolBuilder.buildAsPayloadWithId();
-        for (PayloadWithId payload : payloads) {
-            assertThat(payload.payload.hasStreamFeature()).isTrue();
-            removeTracking.filterStreamFeature(payload.payload.getStreamFeature());
-        }
-        removeTracking.triggerConsumerUpdate();
-    }
-
-    private RemoveTracking<String> getRemoveTracking(
-            Function<StreamFeature, String> transformer, Consumer<List<String>> consumer) {
-        return new RemoveTracking<>(transformer, consumer);
-    }
-
-    @SuppressWarnings("unused")
-    private String nullTransform(StreamFeature streamFeature) {
-        return null;
-    }
-
-    private String simpleTransform(StreamFeature streamFeature) {
-        return streamFeature.getContentId();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/TokenCompletedTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/TokenCompletedTest.java
deleted file mode 100644
index 1daa7b8..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/modelprovider/TokenCompletedTest.java
+++ /dev/null
@@ -1,36 +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.
-
-package org.chromium.chrome.browser.feed.library.api.internal.modelprovider;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link TokenCompleted} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TokenCompletedTest {
-    @Mock
-    private ModelCursor mModelCursor;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-    }
-
-    @Test
-    public void testTokenChange() {
-        TokenCompleted tokenCompleted = new TokenCompleted(mModelCursor);
-        assertThat(tokenCompleted.getCursor()).isEqualTo(mModelCursor);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/ClearAllListenerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/ClearAllListenerTest.java
deleted file mode 100644
index 45bdf54..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/ClearAllListenerTest.java
+++ /dev/null
@@ -1,85 +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.
-
-package org.chromium.chrome.browser.feed.library.api.internal.scope;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.lifecycle.Resettable;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedAppLifecycleListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ClearAllListener} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ClearAllListenerTest {
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-
-    @Mock
-    private Resettable mStore;
-    @Mock
-    private FeedSessionManager mSessionManager;
-    private FakeTaskQueue mFakeTaskQueue;
-    private FeedAppLifecycleListener mFeedAppLifecycleListener;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeTaskQueue.initialize(() -> {});
-        mFeedAppLifecycleListener = new FeedAppLifecycleListener(mFakeThreadUtils);
-    }
-
-    @Test
-    public void testClearAll() {
-        setupClearAllListener();
-        mFeedAppLifecycleListener.onClearAll();
-        verify(mSessionManager).reset();
-        verify(mStore).reset();
-        assertThat(mFakeTaskQueue.resetWasCalled()).isTrue();
-        assertThat(mFakeTaskQueue.completeResetWasCalled()).isTrue();
-    }
-
-    @Test
-    public void testClearAllWithRefresh() {
-        setupClearAllListener();
-        mFeedAppLifecycleListener.onClearAllWithRefresh();
-        verify(mSessionManager).reset();
-        verify(mSessionManager)
-                .triggerRefresh(null, RequestReason.CLEAR_ALL, UiContext.getDefaultInstance());
-        verify(mStore).reset();
-        assertThat(mFakeTaskQueue.resetWasCalled()).isTrue();
-        assertThat(mFakeTaskQueue.completeResetWasCalled()).isTrue();
-    }
-
-    @Test
-    public void testNonClearLifecycld() {
-        setupClearAllListener();
-        mFeedAppLifecycleListener.onEnterForeground();
-        verifyZeroInteractions(mSessionManager);
-        verifyZeroInteractions(mStore);
-    }
-
-    private void setupClearAllListener() {
-        new ClearAllListener(mFakeTaskQueue, mSessionManager, mStore, mFakeThreadUtils,
-                mFeedAppLifecycleListener);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/FeedProcessScopeTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/FeedProcessScopeTest.java
deleted file mode 100644
index f13175e8..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/api/internal/scope/FeedProcessScopeTest.java
+++ /dev/null
@@ -1,99 +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.
-
-package org.chromium.chrome.browser.feed.library.api.internal.scope;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.lifecycle.AppLifecycleListener;
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.network.NetworkClient;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipSupportedApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.knowncontent.FeedKnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.feedobservable.FeedObservable;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedLifecycleListener;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link FeedProcessScope}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedProcessScopeTest {
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private NetworkClient mNetworkClient;
-    @Mock
-    private ApplicationInfo mApplicationInfo;
-    @Mock
-    private TooltipSupportedApi mTooltipSupportedApi;
-    @Mock
-    private ProtocolAdapter mProtocolAdapter;
-    @Mock
-    private RequestManager mRequestManager;
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private Store mStore;
-    @Mock
-    private TaskQueue mTaskQueue;
-    @Mock
-    private AppLifecycleListener mAppLifecycleListener;
-    @Mock
-    private DebugBehavior mDebugBehavior;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private FeedKnownContent mFeedKnownContent;
-    @Mock
-    private FeedExtensionRegistry mFeedExtensionRegistry;
-    @Mock
-    private FeedObservable<FeedLifecycleListener> mFeedLifecycleListenerFeedObservable;
-
-    private final Clock mClock = new FakeClock();
-    private final MainThreadRunner mMainThreadRunner = new MainThreadRunner();
-    private final ThreadUtils mThreadUtils = new ThreadUtils();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private Configuration mConfiguration = new Configuration.Builder().build();
-    private ClearAllListener mClearAllListener;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mClearAllListener = new ClearAllListener(mTaskQueue, mFeedSessionManager, null,
-                mThreadUtils, mFeedLifecycleListenerFeedObservable);
-    }
-
-    @Test
-    public void testDestroy() throws Exception {
-        FeedProcessScope processScope = new FeedProcessScope(mBasicLoggingApi, mNetworkClient,
-                mProtocolAdapter, mRequestManager, mFeedSessionManager, mStore, mTimingUtils,
-                mThreadUtils, mTaskQueue, mMainThreadRunner, mAppLifecycleListener, mClock,
-                mDebugBehavior, mActionManager, mConfiguration, mFeedKnownContent,
-                mFeedExtensionRegistry, mClearAllListener, mTooltipSupportedApi, mApplicationInfo);
-        processScope.onDestroy();
-        verify(mNetworkClient).close();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/BasicStreamTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/BasicStreamTest.java
deleted file mode 100644
index 262b34f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/BasicStreamTest.java
+++ /dev/null
@@ -1,1464 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.shared.stream.Stream.POSITION_NOT_KNOWN;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.app.Activity;
-import android.content.Context;
-import android.util.Base64;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentMetadata;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.KnownContent;
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.imageloader.ImageLoaderApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ZeroStateShowReason;
-import org.chromium.chrome.browser.feed.library.api.host.offlineindicator.OfflineIndicatorApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.SnackbarApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.StreamConfiguration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParserFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.knowncontent.FeedKnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.StreamItemAnimator;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.StreamRecyclerViewAdapter;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.StreamDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.BasicStreamScrollMonitor;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.ScrollRestorer;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater.ViewLoggingUpdater;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.PietManager;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.chrome.browser.feed.library.sharedstream.contentchanged.StreamContentChangedListener;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManagerImpl;
-import org.chromium.chrome.browser.feed.library.sharedstream.deepestcontenttracker.DeepestContentTracker;
-import org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor.StreamOfflineMonitor;
-import org.chromium.chrome.browser.feed.library.sharedstream.piet.PietEventLogger;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer.MenuMeasurer;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObservable;
-import org.chromium.chrome.browser.feed.library.sharedstream.scroll.ScrollListenerNotifier;
-import org.chromium.chrome.browser.feed.library.testing.shadows.ShadowRecycledViewPool;
-import org.chromium.chrome.browser.feed.shared.stream.Header;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ScrollListener;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.libraries.basicstream.internal.StreamSavedInstanceStateProto.StreamSavedInstanceState;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.ScrollStateProto.ScrollState;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason.Reason;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.components.user_prefs.UserPrefsJni;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/** Tests for {@link BasicStream}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecycledViewPool.class})
-@Features.DisableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-public class BasicStreamTest {
-    private static final int START_PADDING = 1;
-    private static final int END_PADDING = 2;
-    private static final int TOP_PADDING = 3;
-    private static final int BOTTOM_PADDING = 4;
-    private static final long LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS = 1000;
-    private static final int ADAPTER_HEADER_COUNT = 5;
-
-    private static final String SESSION_ID = "session-id";
-    private static final ScrollState SCROLL_STATE =
-            ScrollState.newBuilder().setOffset(10).setPosition(10).build();
-    private static final StreamSavedInstanceState SAVED_INSTANCE_STATE =
-            StreamSavedInstanceState.newBuilder()
-                    .setSessionId(SESSION_ID)
-                    .setScrollState(SCROLL_STATE)
-                    .build();
-    private static final long SPINNER_DELAY_MS = 123L;
-    private static final long SPINNER_MINIMUM_SHOW_TIME_MS = 655L;
-    private static final Configuration CONFIGURATION =
-            new Configuration.Builder()
-                    .put(ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS,
-                            LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS)
-                    .put(ConfigKey.SPINNER_DELAY_MS, SPINNER_DELAY_MS)
-                    .put(ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS, SPINNER_MINIMUM_SHOW_TIME_MS)
-                    .build();
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    private StreamConfiguration mStreamConfiguration;
-    @Mock
-    private ModelFeature mModelFeature;
-    @Mock
-    private ModelProviderFactory mModelProviderFactory;
-    @Mock
-    private ModelProvider mInitialModelProvider;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private ModelProvider mRestoredModelProvider;
-    @Mock
-    private PietManager mPietManager;
-    @Mock
-    private SnackbarApi mSnackbarApi;
-    @Mock
-    private StreamDriver mStreamDriver;
-    @Mock
-    private StreamRecyclerViewAdapter mAdapter;
-    @Mock
-    private ScrollListenerNotifier mScrollListenerNotifier;
-    @Mock
-    private ScrollRestorer mNonRestoringScrollRestorer;
-    @Mock
-    private ScrollRestorer mScrollRestorer;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private ContextMenuManagerImpl mContextMenuManager;
-    @Mock
-    private ViewLoggingUpdater mViewLoggingUpdater;
-    @Mock
-    private TooltipApi mTooltipApi;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private UserPrefs.Natives mUserPrefsJniMock;
-    @Mock
-    private Profile mProfile;
-    @Mock
-    private PrefService mPrefService;
-
-    private FakeFeedKnownContent mFakeFeedKnownContent;
-    private LinearLayoutManagerWithFakePositioning mLayoutManager;
-    private Context mContext;
-    private FakeClock mClock;
-    private BasicStreamForTest mBasicStream;
-    private FakeMainThreadRunner mMainThreadRunner;
-    private List<Header> mHeaders;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
-        Profile.setLastUsedProfileForTesting(mProfile);
-        when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService);
-        when(mPrefService.getBoolean(Pref.HAS_REACHED_CLICK_AND_VIEW_ACTIONS_UPLOAD_CONDITIONS))
-                .thenReturn(true);
-
-        mFakeFeedKnownContent = new FakeFeedKnownContent();
-        mHeaders = new ArrayList<>();
-        mHeaders.add(mock(Header.class));
-
-        // TODO: Move header orchestration into separate class.
-        // Purposely using a different header count here as it is possible for size of headers to
-        // change due to swipe to dismiss.  Adapter is source of truth for headers right now.
-        // Ideally we should have a drivers specifically for header management but we don't just
-        // yet.
-        when(mAdapter.getHeaderCount()).thenReturn(ADAPTER_HEADER_COUNT);
-
-        when(mStreamConfiguration.getPaddingStart()).thenReturn(START_PADDING);
-        when(mStreamConfiguration.getPaddingEnd()).thenReturn(END_PADDING);
-        when(mStreamConfiguration.getPaddingTop()).thenReturn(TOP_PADDING);
-        when(mStreamConfiguration.getPaddingBottom()).thenReturn(BOTTOM_PADDING);
-
-        when(mModelProviderFactory.createNew(any(ViewDepthProvider.class), any(UiContext.class)))
-                .thenReturn(mInitialModelProvider, mModelProvider);
-
-        when(mInitialModelProvider.getSessionId()).thenReturn(SESSION_ID);
-
-        when(mScrollRestorer.getScrollStateForScrollRestore(ADAPTER_HEADER_COUNT))
-                .thenReturn(SCROLL_STATE);
-
-        when(mStreamDriver.getLeafFeatureDrivers()).thenReturn(Collections.emptyList());
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mClock = new FakeClock();
-        mMainThreadRunner = FakeMainThreadRunner.create(mClock);
-        mLayoutManager = new LinearLayoutManagerWithFakePositioning(mContext);
-
-        mBasicStream = createBasicStream(mLayoutManager);
-        mBasicStream.onCreate(null);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void testOnCreate_setReachedUploadConditionsBitInActionManager_whenFeatureEnabled() {
-        verify(mActionManager, times(1)).setCanUploadClicksAndViewsWhenNoticeCardIsPresent(true);
-    }
-
-    @Test
-    public void testRecyclerViewSetup() {
-        assertThat(getStreamRecyclerView().getId()).isEqualTo(R.id.feed_stream_recycler_view);
-    }
-
-    @Test
-    public void testOnSessionStart() {
-        mBasicStream.onShow();
-        reset(mAdapter);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mStreamDriver).onDestroy();
-        verify(mAdapter).setDriver(mStreamDriver);
-        assertThat(mBasicStream.mStreamDriverScrollRestorer).isSameInstanceAs(mScrollRestorer);
-    }
-
-    @Test
-    public void testOnSessionStart_logsOnOpenedWithStreamContentAfterOnShow() {
-        mClock = mClock.set(10);
-        mBasicStream.onShow();
-
-        when(mStreamDriver.hasContent()).thenReturn(true);
-        mClock = mClock.set(40);
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi).onOpenedWithContent(30, 0);
-    }
-
-    @Test
-    public void testOnSessionStart_logsOnOpenedWithStreamContentAfterOnShow_whenRestoring() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        mBasicStream = createBasicStream(new LinearLayoutManager(mContext));
-
-        mBasicStream.onCreate(savedInstanceState);
-
-        mClock.set(15L);
-        mBasicStream.onShow();
-
-        when(mStreamDriver.hasContent()).thenReturn(true);
-        mClock.advance(5L);
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi).onOpenedWithContent(5, 0);
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotLogOnOpenedWithStreamContentAfterInitialOnShow() {
-        mBasicStream.onShow();
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        reset(mBasicLoggingApi);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi, never()).onOpenedWithContent(anyInt(), anyInt());
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotLogOnOpenedWithStreamContent_IfOnErrorLogsNoContent() {
-        mBasicStream.onShow();
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-        reset(mBasicLoggingApi);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi, never()).onOpenedWithContent(anyInt(), anyInt());
-    }
-
-    @Test
-    public void testOnSessionStart_logsOnOpenedWithNoContent_ifStreamDriverDoesNotHaveContent() {
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void
-    testOnSessionStart_doesNotUseNewStreamDriver_ifBothStreamDriversAreShowingZeroState() {
-        StreamDriver newStreamDriver = mock(StreamDriver.class);
-        mBasicStream.onShow();
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-        reset(mAdapter, mStreamDriver);
-        when(mStreamDriver.isZeroStateBeingShown()).thenReturn(true);
-        when(newStreamDriver.isZeroStateBeingShown()).thenReturn(true);
-
-        mBasicStream.mStreamDriver = newStreamDriver;
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mStreamDriver).setModelProviderForZeroState(mInitialModelProvider);
-        verify(mStreamDriver, never()).onDestroy();
-        verify(newStreamDriver).onDestroy();
-        verify(mAdapter, never()).setDriver(any(StreamDriver.class));
-    }
-
-    @Test
-    public void testOnSessionFinished() {
-        mBasicStream.onShow();
-        reset(mStreamDriver);
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        verify(mScrollRestorer).abandonRestoringScroll();
-        verify(mInitialModelProvider).unregisterObserver(mBasicStream);
-        verify(mModelProviderFactory, times(2))
-                .createNew(any(ViewDepthProvider.class), any(UiContext.class));
-        verify(mModelProvider).registerObserver(mBasicStream);
-    }
-
-    @Test
-    public void testOnError_showsZeroState() {
-        mBasicStream.onShow();
-        reset(mStreamDriver);
-
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-
-        verify(mScrollRestorer).abandonRestoringScroll();
-        verify(mStreamDriver).showZeroState(ZeroStateShowReason.ERROR);
-    }
-
-    @Test
-    public void testOnError_logsOnOpenedWithNoContent() {
-        when(mStreamDriver.hasContent()).thenReturn(false);
-        mClock = mClock.set(10);
-        mBasicStream.onShow();
-
-        mClock = mClock.set(10 + LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS);
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-
-        verify(mBasicLoggingApi).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnError_doesNotDoubleLogOnOpenedWithNoContent() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.READY);
-        when(mInitialModelProvider.getRootFeature()).thenReturn(null);
-
-        mBasicStream.onShow();
-        // Trigger onOpenedWithNoContent logging through updating the driver.
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        reset(mBasicLoggingApi);
-
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnSessionStart_logsOnOpenedWithNoImmediateContent() {
-        mBasicStream.onShow();
-
-        // Advance so that the spinner starts showing
-        mClock.advance(SPINNER_DELAY_MS);
-
-        // Advance so that is has taken long enough that onOpenedWithNoImmediateContent is logged.
-        mClock.advance(LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi).onOpenedWithNoImmediateContent();
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotLogOnOpenedWithNoImmediateContent_ifNotWithinThreshold() {
-        mBasicStream.onShow();
-
-        mClock.advance(LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS - 1);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoImmediateContent();
-    }
-
-    @Test
-    public void testOnSessionStart_logsOnOpenedWithNoContent() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.READY);
-        when(mInitialModelProvider.getRootFeature()).thenReturn(null);
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnShow_doesNotLogOnOpenedWithNoContent_ifModelProviderNotReady() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.INITIALIZING);
-        when(mInitialModelProvider.getRootFeature()).thenReturn(null);
-
-        mBasicStream.onShow();
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnShow_doesNotLogOnOpenedWithNoContent_ifRootFeatureNotNull() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.READY);
-        when(mInitialModelProvider.getRootFeature()).thenReturn(mModelFeature);
-
-        mBasicStream.onShow();
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotDoubleLogOnOpenedWithNoContent() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.READY);
-        when(mInitialModelProvider.getRootFeature()).thenReturn(null);
-
-        mBasicStream.onShow();
-        // Trigger onOpenedWithNoContent logging through onError.
-        mBasicStream.onError(
-                new ModelError(ErrorType.NO_CARDS_ERROR, /* continuationToken= */ null));
-        reset(mBasicLoggingApi);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotLogOnOpenedWithNoImmediateContentAfterInitialOnShow() {
-        mBasicStream.onShow();
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        reset(mBasicLoggingApi);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mBasicLoggingApi, never()).onOpenedWithNoImmediateContent();
-    }
-
-    @Test
-    public void testOnError_doesNotShowZeroState() {
-        mBasicStream.onShow();
-
-        assertThatRunnable(
-                ()
-                        -> mBasicStream.onError(new ModelError(ErrorType.PAGINATION_ERROR,
-                                /* continuationToken= */ null)))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testLifecycle_onCreateWithStringCalledOnlyOnce() {
-        // onCreate is called once in setup
-        assertThatRunnable(() -> mBasicStream.onCreate(""))
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-
-    @Test
-    public void testLifecycle_getViewBeforeOnCreateCrashes() {
-        // create BasicStream that has not had onCreate() called.
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        assertThatRunnable(() -> mBasicStream.getView())
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-
-    @Test
-    public void testLifecycle_onCreate_onDestroy() {
-        mBasicStream.onDestroy();
-        verify(mInitialModelProvider, never()).invalidate();
-    }
-
-    @Test
-    public void testLifecycle_onCreate_onShow_onHide_onDestroy() {
-        mBasicStream.onShow();
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-        verify(mAdapter).onDestroy();
-        verify(mInitialModelProvider, never()).invalidate();
-        verify(mInitialModelProvider).detachModelProvider();
-        assertThat(mFakeFeedKnownContent.mListeners).isEmpty();
-    }
-
-    @Test
-    public void testOnDestroy_destroysStreamDriver() {
-        mBasicStream.onShow();
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        reset(mStreamDriver);
-
-        mBasicStream.onDestroy();
-
-        verify(mStreamDriver).onDestroy();
-    }
-
-    @Test
-    public void testOnDestroy_unregistersOnLayoutChangeListener() {
-        // initial layout from 0, 0 will not rebind.
-        getStreamRecyclerView().layout(0, 0, 100, 300);
-        // change the width / height to simulate device rotation
-        getStreamRecyclerView().layout(0, 0, 300, 100);
-        verify(mAdapter).rebind();
-
-        reset(mAdapter);
-        mBasicStream.onDestroy();
-
-        // change the width / height to simulate device rotation
-        getStreamRecyclerView().layout(0, 0, 100, 300);
-        verify(mAdapter, never()).rebind();
-    }
-
-    @Test
-    public void testOnLayoutChange_signalsViewActionManager() {
-        getStreamRecyclerView().layout(0, 0, 100, 300);
-        verify(mActionManager, times(1)).onLayoutChange(); // Initial layout.
-
-        getStreamRecyclerView().layout(0, 0, 300, 100); // New layout.
-        verify(mActionManager, times(2)).onLayoutChange();
-    }
-
-    @Test
-    public void testOnDestroy_deregistersSessionListener() {
-        mBasicStream.onShow();
-
-        mBasicStream.onDestroy();
-
-        // Once for BasicStream, once for the session listener.
-        verify(mInitialModelProvider, times(2)).unregisterObserver(any());
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void testOnDestroy_setReachedUploadConditionsBitInActionManager_whenFeatureEnabled() {
-        mBasicStream.onDestroy();
-
-        // Verify that the upload bit is updated one time on #setupRecyclerView and one time
-        // on #onDestroy.
-        verify(mActionManager, times(2)).setCanUploadClicksAndViewsWhenNoticeCardIsPresent(true);
-    }
-
-    @Test
-    public void testGetSavedInstanceStateString_beforeShow() throws InvalidProtocolBufferException {
-        StreamSavedInstanceState savedInstanceState = StreamSavedInstanceState.parseFrom(
-                decodeSavedInstanceStateString(mBasicStream.getSavedInstanceStateString()));
-        assertThat(savedInstanceState.hasSessionId()).isFalse();
-        assertThat(savedInstanceState.getScrollState()).isEqualTo(SCROLL_STATE);
-    }
-
-    @Test
-    public void testGetSavedInstanceStateString_afterOnShow_beforeSessionStart()
-            throws InvalidProtocolBufferException {
-        when(mInitialModelProvider.getSessionId()).thenReturn(null);
-
-        mBasicStream.onShow();
-
-        StreamSavedInstanceState savedInstanceState = StreamSavedInstanceState.parseFrom(
-                decodeSavedInstanceStateString(mBasicStream.getSavedInstanceStateString()));
-        assertThat(savedInstanceState.hasSessionId()).isFalse();
-        assertThat(savedInstanceState.getScrollState()).isEqualTo(SCROLL_STATE);
-    }
-
-    @Test
-    public void testGetSavedInstanceStateString_afterOnShow_afterSessionStart()
-            throws InvalidProtocolBufferException {
-        mBasicStream.onShow();
-
-        StreamSavedInstanceState savedInstanceState = StreamSavedInstanceState.parseFrom(
-                decodeSavedInstanceStateString(mBasicStream.getSavedInstanceStateString()));
-        assertThat(savedInstanceState.getSessionId()).isEqualTo(SESSION_ID);
-        assertThat(savedInstanceState.getScrollState()).isEqualTo(SCROLL_STATE);
-    }
-
-    @Test
-    public void testGetSavedInstanceStateString_noScrollRestoreBundle()
-            throws InvalidProtocolBufferException {
-        mBasicStream.onShow();
-
-        when(mScrollRestorer.getScrollStateForScrollRestore(ADAPTER_HEADER_COUNT)).thenReturn(null);
-        StreamSavedInstanceState savedInstanceState = StreamSavedInstanceState.parseFrom(
-                decodeSavedInstanceStateString(mBasicStream.getSavedInstanceStateString()));
-        assertThat(savedInstanceState.hasScrollState()).isFalse();
-    }
-
-    @Test
-    public void testRestore() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-
-        mBasicStream.onShow();
-
-        verify(mRestoredModelProvider).registerObserver(mBasicStream);
-    }
-
-    @Test
-    public void testRestore_withStringSavedState() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-
-        mBasicStream.onShow();
-
-        verify(mRestoredModelProvider).registerObserver(mBasicStream);
-    }
-
-    @Test
-    public void testRestore_doesNotShowZeroState() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        reset(mStreamDriver);
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-
-        mBasicStream.onShow();
-
-        verify(mStreamDriver, never()).showZeroState(/* zeroStateShowReason= */ anyInt());
-        verify(mStreamDriver, never()).showSpinner();
-    }
-
-    @Test
-    public void testRestore_showsZeroStateIfNoSessionToRestore() {
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate("");
-
-        mBasicStream.onShow();
-
-        verify(mStreamDriver, never()).showSpinner();
-
-        mClock.advance(SPINNER_DELAY_MS);
-
-        verify(mStreamDriver).showSpinner();
-    }
-
-    @Test
-    public void testRestore_invalidSession() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-        mBasicStream.onShow();
-
-        verify(mModelProvider).registerObserver(mBasicStream);
-    }
-
-    @Test
-    public void testRestore_invalidBase64Encoding() {
-        mBasicStream.onShow();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        assertThatRunnable(() -> mBasicStream.onCreate("=invalid"))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testRestore_invalidProtocolBuffer() {
-        mBasicStream.onShow();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        assertThatRunnable(()
-                                   -> mBasicStream.onCreate(Base64.encodeToString(
-                                           "invalid".getBytes(UTF_8), Base64.DEFAULT)))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testRestore_createsStreamDriver() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-
-        mBasicStream.onShow();
-
-        assertThat(mBasicStream.mStreamDriverRestoring).isTrue();
-    }
-
-    @Test
-    public void testRestore_createsStreamDriver_afterFailure() {
-        mBasicStream.onShow();
-
-        String savedInstanceState = mBasicStream.getSavedInstanceStateString();
-
-        mBasicStream.onHide();
-        mBasicStream.onDestroy();
-
-        when(mModelProviderFactory.create(SESSION_ID, UiContext.getDefaultInstance()))
-                .thenReturn(mRestoredModelProvider);
-
-        mBasicStream = createBasicStream(new LinearLayoutManagerWithFakePositioning(mContext));
-        mBasicStream.onCreate(savedInstanceState);
-
-        // onSessionFinish indicates the restore has failed.
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        mBasicStream.onShow();
-
-        assertThat(mBasicStream.mStreamDriverRestoring).isFalse();
-    }
-
-    @Test
-    public void testTrim() {
-        ShadowRecycledViewPool viewPool =
-                Shadow.extract(getStreamRecyclerView().getRecycledViewPool());
-
-        // RecyclerView ends up clearing the pool initially when the adapter is set on the
-        // RecyclerView. Verify that has happened before anything else
-        assertThat(viewPool.getClearCallCount()).isEqualTo(1);
-
-        mBasicStream.trim();
-
-        verify(mPietManager).purgeRecyclerPools();
-
-        // We expect the clear() call to be 2 as RecyclerView ends up clearing the pool initially
-        // when the adapter is set on the RecyclerView.  So one call for that and one call for
-        // trim() call on stream.
-        assertThat(viewPool.getClearCallCount()).isEqualTo(2);
-    }
-
-    @Test
-    public void testLayoutChanges() {
-        // initial layout from 0, 0 will not rebind.
-        getStreamRecyclerView().layout(0, 0, 100, 300);
-        verify(mAdapter, never()).rebind();
-        // change the width / height to simulate device rotation
-        getStreamRecyclerView().layout(0, 0, 300, 100);
-        verify(mAdapter).rebind();
-    }
-
-    @Test
-    public void testAddScrollListener() {
-        ScrollListener scrollListener = mock(ScrollListener.class);
-        mBasicStream.addScrollListener(scrollListener);
-        verify(mScrollListenerNotifier).addScrollListener(scrollListener);
-    }
-
-    @Test
-    public void testRemoveScrollListener() {
-        ScrollListener scrollListener = mock(ScrollListener.class);
-        mBasicStream.removeScrollListener(scrollListener);
-        verify(mScrollListenerNotifier).removeScrollListener(scrollListener);
-    }
-
-    @Test
-    public void testIsChildAtPositionVisible() {
-        mLayoutManager.mFirstVisiblePosition = 0;
-        mLayoutManager.mLastVisiblePosition = 1;
-        assertThat(mBasicStream.isChildAtPositionVisible(-2)).isFalse();
-        assertThat(mBasicStream.isChildAtPositionVisible(-1)).isFalse();
-        assertThat(mBasicStream.isChildAtPositionVisible(0)).isTrue();
-        assertThat(mBasicStream.isChildAtPositionVisible(1)).isTrue();
-        assertThat(mBasicStream.isChildAtPositionVisible(2)).isFalse();
-    }
-
-    @Test
-    public void testIsChildAtPositionVisible_nothingVisible() {
-        assertThat(mBasicStream.isChildAtPositionVisible(0)).isFalse();
-    }
-
-    @Test
-    public void testIsChildAtPositionVisible_validTop() {
-        mLayoutManager.mFirstVisiblePosition = 0;
-        assertThat(mBasicStream.isChildAtPositionVisible(0)).isFalse();
-    }
-
-    @Test
-    public void testIsChildAtPositionVisible_validBottom() {
-        mLayoutManager.mLastVisiblePosition = 1;
-        assertThat(mBasicStream.isChildAtPositionVisible(0)).isFalse();
-    }
-
-    @Test
-    public void testGetChildTopAt_noVisibleChild() {
-        assertThat(mBasicStream.getChildTopAt(0)).isEqualTo(POSITION_NOT_KNOWN);
-    }
-
-    @Test
-    public void testGetChildTopAt_noChild() {
-        mLayoutManager.mFirstVisiblePosition = 0;
-        mLayoutManager.mLastVisiblePosition = 1;
-        assertThat(mBasicStream.getChildTopAt(0)).isEqualTo(POSITION_NOT_KNOWN);
-    }
-
-    @Test
-    public void testGetChildTopAt() {
-        mLayoutManager.mFirstVisiblePosition = 0;
-        mLayoutManager.mLastVisiblePosition = 1;
-        View view = new FrameLayout(mContext);
-        mLayoutManager.addChildToPosition(0, view);
-
-        assertThat(mBasicStream.getChildTopAt(0)).isEqualTo(view.getTop());
-    }
-
-    @Test
-    public void testStreamContentVisible() {
-        mBasicStream.setStreamContentVisibility(false);
-        verify(mAdapter).setStreamContentVisible(false);
-
-        reset(mAdapter);
-        mBasicStream.setStreamContentVisibility(true);
-        verify(mAdapter).setStreamContentVisible(true);
-    }
-
-    @Test
-    public void testStreamContentVisible_notifiesItemAnimator_notVisible() {
-        mBasicStream.setStreamContentVisibility(false);
-
-        assertThat(((StreamItemAnimator) getStreamRecyclerView().getItemAnimator())
-                           .getStreamContentVisibility())
-                .isFalse();
-    }
-
-    @Test
-    public void testStreamContentVisible_notifiesItemAnimator_visible() {
-        mBasicStream.setStreamContentVisibility(true);
-
-        assertThat(((StreamItemAnimator) getStreamRecyclerView().getItemAnimator())
-                           .getStreamContentVisibility())
-                .isTrue();
-    }
-
-    @Test
-    public void testSetStreamContentVisibility_createsModelProvider_ifContentNotVisible() {
-        mBasicStream.setStreamContentVisibility(false);
-        mBasicStream.onShow();
-
-        verifyNoMoreInteractions(mModelProvider);
-
-        mBasicStream.setStreamContentVisibility(true);
-
-        verify(mModelProviderFactory).createNew(any(ViewDepthProvider.class), any(UiContext.class));
-    }
-
-    @Test
-    public void testSetStreamContentVisibility_resetsViewLogging() {
-        mBasicStream.setStreamContentVisibility(false);
-        mBasicStream.onShow();
-
-        mBasicStream.setStreamContentVisibility(true);
-
-        verify(mViewLoggingUpdater).resetViewTracking();
-    }
-
-    @Test
-    public void testSetStreamContentVisibility_trueMultipleTimes_doesNotResetViewLogging() {
-        mBasicStream.setStreamContentVisibility(true);
-        mBasicStream.onShow();
-
-        mBasicStream.setStreamContentVisibility(true);
-
-        verify(mViewLoggingUpdater, never()).resetViewTracking();
-    }
-
-    @Test
-    public void testTriggerRefresh() {
-        mBasicStream.onShow();
-
-        reset(mAdapter, mStreamDriver);
-
-        mBasicStream.triggerRefresh();
-
-        verify(mInitialModelProvider).triggerRefresh(RequestReason.HOST_REQUESTED);
-        verify(mStreamDriver).showSpinner();
-    }
-
-    @Test
-    public void testTriggerRefresh_beforeOnShow() {
-        mBasicStream.triggerRefresh();
-
-        verify(mInitialModelProvider, never()).triggerRefresh(anyInt());
-        verify(mStreamDriver, never()).showSpinner();
-    }
-
-    @Test
-    public void testAddOnContentChangedListener() {
-        ContentChangedListener contentChangedListener = mock(ContentChangedListener.class);
-
-        mBasicStream.addOnContentChangedListener(contentChangedListener);
-        mBasicStream.mContentChangedListener.onContentChanged();
-
-        verify(contentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testRemoveOnContentChangedListener() {
-        ContentChangedListener contentChangedListener = mock(ContentChangedListener.class);
-
-        mBasicStream.addOnContentChangedListener(contentChangedListener);
-        mBasicStream.removeOnContentChangedListener(contentChangedListener);
-
-        mBasicStream.mContentChangedListener.onContentChanged();
-
-        verify(contentChangedListener, never()).onContentChanged();
-    }
-
-    @Test
-    public void testOnShow_nonRestoringRestorer() {
-        mBasicStream.onShow();
-
-        assertThat(mBasicStream.mStreamDriverScrollRestorer).isEqualTo(mNonRestoringScrollRestorer);
-    }
-
-    @Test
-    public void testOnShow_restoringScrollRestorer() {
-        when(mInitialModelProvider.getCurrentState()).thenReturn(State.READY);
-        mBasicStream.onShow();
-
-        assertThat(mBasicStream.mStreamDriverScrollRestorer).isEqualTo(mScrollRestorer);
-    }
-
-    @Test
-    public void testOnShow_setShown() {
-        mBasicStream.onShow();
-
-        verify(mAdapter).setShown(true);
-    }
-
-    @Test
-    public void testOnShow_doesNotCreateModelProvider_ifStreamContentNotVisible() {
-        mBasicStream.setStreamContentVisibility(false);
-
-        mBasicStream.onShow();
-
-        verifyNoMoreInteractions(mModelProviderFactory);
-        verify(mAdapter, never()).setDriver(any(StreamDriver.class));
-    }
-
-    @Test
-    public void testOnShow_restoresScrollPosition_ifStreamContentNotVisible() {
-        mBasicStream.setStreamContentVisibility(false);
-
-        mBasicStream.onShow();
-
-        verify(mScrollRestorer).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testOnSessionStart_showsContentImmediately_ifNotInitialLoad() {
-        mBasicStream.onShow();
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        reset(mAdapter);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        verify(mAdapter).setDriver(mStreamDriver);
-    }
-
-    @Test
-    public void testOnSessionStart_showsContentImmediately_ifSpinnerTimeElapsed() {
-        mBasicStream.onShow();
-        reset(mAdapter);
-
-        // Advance so the spinner is shown.
-        mClock.advance(SPINNER_DELAY_MS);
-
-        // Advance so that it has shown long enough
-        mClock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        // The adapter should be immediately set in onSesssionStart
-        verify(mAdapter).setDriver(mStreamDriver);
-    }
-
-    @Test
-    public void testOnSessionStart_showsContentWithDelay_ifSpinnerTimeNotElapsed() {
-        mBasicStream.onShow();
-
-        // Advance so the spinner is shown.
-        mClock.advance(SPINNER_DELAY_MS);
-
-        reset(mAdapter);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        verify(mAdapter, never()).setDriver(any(StreamDriver.class));
-
-        // Advance so that it has shown long enough.
-        mClock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
-
-        verify(mAdapter).setDriver(mStreamDriver);
-    }
-
-    @Test
-    public void testOnSessionStart_doesNotShowContent_ifSessionFinishesBeforeSpinnerTimeElapsed() {
-        mClock.set(10);
-
-        mBasicStream.onShow();
-
-        reset(mAdapter);
-
-        // Advance so the spinner is shown.
-        mClock.advance(SPINNER_DELAY_MS);
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        // Advance so that it has shown long enough.
-        mClock.advance(SPINNER_MINIMUM_SHOW_TIME_MS);
-
-        verify(mAdapter, never()).setDriver(mStreamDriver);
-    }
-
-    @Test
-    public void testOnSessionStart_showsZeroState_ifFeatureDriversEmpty() {
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        verify(mStreamDriver).showZeroState(ZeroStateShowReason.NO_CONTENT);
-    }
-
-    @Test
-    public void testOnShow_delaysShowingZeroState_onInitialLoad() {
-        mBasicStream.onShow();
-
-        verify(mStreamDriver, never()).showZeroState(/* zeroStateShowReason= */ anyInt());
-        verify(mStreamDriver, never()).showSpinner();
-
-        // Advance so the spinner is shown.
-        mClock.advance(SPINNER_DELAY_MS);
-
-        verify(mStreamDriver).showSpinner();
-    }
-
-    @Test
-    public void testOnShow_delaysShowingZeroState_configured_delay_time() {
-        mBasicStream.onShow();
-
-        verify(mStreamDriver, never()).showZeroState(/* zeroStateShowReason= */ anyInt());
-        verify(mStreamDriver, never()).showSpinner();
-
-        // Advance so the spinner is shown.
-        mClock.advance(SPINNER_DELAY_MS);
-
-        verify(mStreamDriver).showSpinner();
-    }
-
-    @Test
-    public void testOnHide_setShown() {
-        mBasicStream.onShow();
-        reset(mAdapter);
-
-        mBasicStream.onHide();
-
-        verify(mAdapter).setShown(false);
-    }
-
-    @Test
-    public void testOnShow_signalsViewActionManager() {
-        mBasicStream.onShow();
-        verify(mActionManager).onShow();
-    }
-
-    @Test
-    public void testOnHide_signalsViewActionManager() {
-        mBasicStream.onHide();
-        verify(mActionManager).onHide();
-    }
-
-    @Test
-    public void testOnHide_dismissesPopup() {
-        mBasicStream.onHide();
-        verify(mContextMenuManager).dismissPopup();
-    }
-
-    @Test
-    public void onSessionFinished_afterOnDestroy_unregistersOnce() {
-        mBasicStream.onShow();
-
-        mBasicStream.onDestroy();
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        // Should only occur once between onDestroy() and onSessionFinished()
-        verify(mInitialModelProvider).unregisterObserver(mBasicStream);
-    }
-
-    @Test
-    public void onDestroyTwice_unregistersOnce() {
-        mBasicStream.onShow();
-
-        mBasicStream.onDestroy();
-        mBasicStream.onDestroy();
-
-        // Should only occur once between multiple onDestroy() calls
-        verify(mInitialModelProvider).unregisterObserver(mBasicStream);
-    }
-
-    @Test
-    public void onSessionStart_propagatesUiContext() {
-        mBasicStream.onShow();
-
-        UiRefreshReason uiRefreshReason =
-                UiRefreshReason.newBuilder().setReason(Reason.ZERO_STATE).build();
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        mBasicStream.onSessionFinished(
-                UiContext.newBuilder()
-                        .setExtension(UiRefreshReason.uiRefreshReasonExtension, uiRefreshReason)
-                        .build());
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        assertThat(mBasicStream.mStreamDriverUiRefreshReason).isEqualTo(uiRefreshReason);
-    }
-
-    @Test
-    public void onShow_whileStreamContentNotVisible_logsOpenedWithNoContent() {
-        mBasicStream.setStreamContentVisibility(false);
-
-        mBasicStream.onShow();
-
-        verify(mBasicLoggingApi).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void onShow_whileStreamContentNotVisible_twice_logsOpenedWithNoContentOnce() {
-        mBasicStream.setStreamContentVisibility(false);
-
-        mBasicStream.onShow();
-        mBasicStream.onHide();
-        mBasicStream.onShow();
-
-        verify(mBasicLoggingApi).onOpenedWithNoContent();
-    }
-
-    @Test
-    public void failToRestoreSession_doesntShowSpinnerImmediately() {
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        verify(mStreamDriver, never()).showSpinner();
-    }
-
-    @Test
-    public void failToRestoreSession_showsSpinnerAfterDelay() {
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        mClock.advance(SPINNER_DELAY_MS);
-
-        verify(mStreamDriver).showSpinner();
-    }
-
-    @Test
-    public void startSession_quicklyFinish_doesntShowInitialSpinner() {
-        mBasicStream.onShow();
-
-        // Advance, but not enough so that the spinner shows.
-        mClock.advance(SPINNER_DELAY_MS / 2);
-
-        // Start the session. At this point, the spinner enqueued by onShow() should never be shown.
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-
-        // Finish the session immediately. As we had started a session and the user had presumably
-        // seen content, we shouldn't trigger the spinner enqueued by onShow().
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        // It has now been long enough since onShow that the spinner would show if onSessionStart()
-        // hadn't been called first.
-        mClock.advance(SPINNER_DELAY_MS / 2 + 1);
-
-        verify(mStreamDriver, never()).showSpinner();
-    }
-
-    @Test
-    public void startSession_thenFinish_showsSpinnerAfterDelay() {
-        mBasicStream.onShow();
-
-        mBasicStream.onSessionStart(UiContext.getDefaultInstance());
-        mBasicStream.onSessionFinished(UiContext.getDefaultInstance());
-
-        verify(mStreamDriver, never()).showSpinner();
-
-        mClock.advance(SPINNER_DELAY_MS);
-
-        verify(mStreamDriver).showSpinner();
-    }
-
-    private byte[] decodeSavedInstanceStateString(String savedInstanceState) {
-        return Base64.decode(savedInstanceState, Base64.DEFAULT);
-    }
-
-    private RecyclerView getStreamRecyclerView() {
-        return (RecyclerView) mBasicStream.getView();
-    }
-
-    private BasicStreamForTest createBasicStream(LinearLayoutManager layoutManager) {
-        return new BasicStreamForTest(mContext, mStreamConfiguration, mock(CardConfiguration.class),
-                mock(ImageLoaderApi.class), mock(ActionParserFactory.class), mock(ActionApi.class),
-                mock(CustomElementProvider.class), DebugBehavior.VERBOSE, new ThreadUtils(),
-                mHeaders, mClock, mModelProviderFactory, new HostBindingProvider(), mActionManager,
-                CONFIGURATION, layoutManager, mock(OfflineIndicatorApi.class), mStreamDriver);
-    }
-
-    private class BasicStreamForTest extends BasicStream {
-        private final LinearLayoutManager mLayoutManager;
-        private StreamDriver mStreamDriver;
-        private boolean mStreamDriverRestoring;
-
-        private ScrollRestorer mStreamDriverScrollRestorer;
-        private StreamContentChangedListener mContentChangedListener;
-        private UiRefreshReason mStreamDriverUiRefreshReason;
-
-        public BasicStreamForTest(Context context, StreamConfiguration streamConfiguration,
-                CardConfiguration cardConfiguration, ImageLoaderApi imageLoaderApi,
-                ActionParserFactory actionParserFactory, ActionApi actionApi,
-                /*@Nullable*/ CustomElementProvider customElementProvider,
-                DebugBehavior debugBehavior, ThreadUtils threadUtils, List<Header> headers,
-                Clock clock, ModelProviderFactory modelProviderFactory,
-                /*@Nullable*/ HostBindingProvider hostBindingProvider, ActionManager actionManager,
-                Configuration configuration, LinearLayoutManager layoutManager,
-                OfflineIndicatorApi offlineIndicatorApi, StreamDriver streamDriver) {
-            super(context, streamConfiguration, cardConfiguration, imageLoaderApi,
-                    actionParserFactory, actionApi, customElementProvider, debugBehavior,
-                    threadUtils, headers, clock, modelProviderFactory, hostBindingProvider,
-                    actionManager, configuration, mSnackbarApi, mBasicLoggingApi,
-                    offlineIndicatorApi,
-
-                    mMainThreadRunner, mFakeFeedKnownContent, mTooltipApi,
-                    /* isBackgroundDark= */ false, /* isPlaceholderShown= */ false);
-            this.mLayoutManager = layoutManager;
-            this.mStreamDriver = streamDriver;
-        }
-
-        @Override
-        PietManager createPietManager(Context context, CardConfiguration cardConfiguration,
-                ImageLoaderApi imageLoaderApi,
-                /*@Nullable*/ CustomElementProvider customElementProvider,
-                DebugBehavior debugBehavior, Clock clock,
-                /*@Nullable*/ HostBindingProvider hostBindingProvider,
-                StreamOfflineMonitor streamOfflineMonitor, Configuration config,
-                boolean isBackgroundDark) {
-            return mPietManager;
-        }
-
-        @Override
-        StreamDriver createStreamDriver(ActionApi actionApi, ActionManager actionManager,
-                ActionParserFactory actionParserFactory, ModelProvider modelProvider,
-                ThreadUtils threadUtils, Clock clock, Configuration configuration, Context context,
-                SnackbarApi snackbarApi, ContentChangedListener contentChangedListener,
-                ScrollRestorer scrollRestorer, BasicLoggingApi basicLoggingApi,
-                StreamOfflineMonitor streamOfflineMonitor, FeedKnownContent feedKnownContent,
-                ContextMenuManager contextMenuManager, boolean restoring, boolean isInitialLoad,
-                MainThreadRunner mainThreadRunner, TooltipApi tooltipApi,
-                UiRefreshReason uiRefreshReason, ScrollListenerNotifier scrollListenerNotifier) {
-            mStreamDriverScrollRestorer = scrollRestorer;
-            mStreamDriverRestoring = restoring;
-            this.mStreamDriverUiRefreshReason = uiRefreshReason;
-            return mStreamDriver;
-        }
-
-        @Override
-        StreamContentChangedListener createStreamContentChangedListener() {
-            mContentChangedListener = new StreamContentChangedListener();
-            return mContentChangedListener;
-        }
-
-        @Override
-        StreamRecyclerViewAdapter createRecyclerViewAdapter(Context context,
-                CardConfiguration cardConfiguration, PietManager pietManager,
-                DeepestContentTracker deepestContentTracker,
-                StreamContentChangedListener streamContentChangedListener,
-                ScrollObservable scrollObservable, Configuration configuration,
-                PietEventLogger pietEventLogger) {
-            return mAdapter;
-        }
-
-        @Override
-        ScrollListenerNotifier createScrollListenerNotifier(
-                ContentChangedListener contentChangedListener,
-                BasicStreamScrollMonitor scrollMonitor, MainThreadRunner mainThreadRunner) {
-            return mScrollListenerNotifier;
-        }
-
-        @Override
-        LinearLayoutManager createRecyclerViewLayoutManager(Context context) {
-            return mLayoutManager;
-        }
-
-        @Override
-        ScrollRestorer createScrollRestorer(Configuration configuration, RecyclerView recyclerView,
-                ScrollListenerNotifier scrollListenerNotifier,
-                /*@Nullable*/ ScrollState scrollState) {
-            return mScrollRestorer;
-        }
-
-        @Override
-        ScrollRestorer createNonRestoringScrollRestorer(Configuration configuration,
-                RecyclerView recyclerView, ScrollListenerNotifier scrollListenerNotifier) {
-            return mNonRestoringScrollRestorer;
-        }
-
-        @Override
-        ContextMenuManagerImpl createContextMenuManager(
-                RecyclerView recyclerView, MenuMeasurer menuMeasurer) {
-            return mContextMenuManager;
-        }
-
-        @Override
-        ViewLoggingUpdater createViewLoggingUpdater() {
-            return mViewLoggingUpdater;
-        }
-    }
-
-    private class LinearLayoutManagerWithFakePositioning extends LinearLayoutManager {
-        private final List<View> mChildMap;
-        private int mFirstVisiblePosition = RecyclerView.NO_POSITION;
-        private int mLastVisiblePosition = RecyclerView.NO_POSITION;
-
-        public LinearLayoutManagerWithFakePositioning(Context context) {
-            super(context);
-            mChildMap = new ArrayList<>();
-        }
-
-        @Override
-        public int findFirstVisibleItemPosition() {
-            return mFirstVisiblePosition;
-        }
-
-        @Override
-        public int findLastVisibleItemPosition() {
-            return mLastVisiblePosition;
-        }
-
-        @Override
-        public View findViewByPosition(int i) {
-            if (i < 0 || i >= mChildMap.size()) {
-                return null;
-            }
-            return mChildMap.get(i);
-        }
-
-        private void addChildToPosition(int position, View child) {
-            mChildMap.add(position, child);
-        }
-    }
-
-    private static class FakeFeedKnownContent implements FeedKnownContent {
-        private final Set<KnownContent.Listener> mListeners = new HashSet<>();
-
-        @Override
-        public void getKnownContent(Consumer<List<ContentMetadata>> knownContentConsumer) {}
-
-        @Override
-        public void addListener(KnownContent.Listener listener) {
-            mListeners.add(listener);
-        }
-
-        @Override
-        public void removeListener(KnownContent.Listener listener) {
-            mListeners.remove(listener);
-        }
-
-        @Override
-        public Listener getKnownContentHostNotifier() {
-            return null;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemAnimatorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemAnimatorTest.java
deleted file mode 100644
index 020bf63..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemAnimatorTest.java
+++ /dev/null
@@ -1,79 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ViewActionManager;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.FeedViewHolder;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link StreamItemAnimator}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamItemAnimatorTest {
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private ViewActionManager mViewActionManager;
-    @Mock
-    private FeedViewHolder mFeedViewHolder;
-
-    private StreamItemAnimatorForTest mStreamItemAnimator;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mStreamItemAnimator =
-                new StreamItemAnimatorForTest(mContentChangedListener, mViewActionManager);
-    }
-
-    @Test
-    public void testOnAnimationFinished() {
-        mStreamItemAnimator.onAnimationFinished(mFeedViewHolder);
-        verify(mContentChangedListener).onContentChanged();
-        verify(mViewActionManager, never()).onAnimationFinished();
-    }
-
-    @Test
-    public void testOnAnimationFinished_visible() {
-        mStreamItemAnimator.setStreamVisibility(true);
-        mStreamItemAnimator.onAnimationFinished(mFeedViewHolder);
-        verify(mContentChangedListener).onContentChanged();
-        verify(mViewActionManager).onAnimationFinished();
-    }
-
-    @Test
-    public void setStreamVisibility_toVisible_endsAnimations() {
-        mStreamItemAnimator.setStreamVisibility(true);
-        assertThat(mStreamItemAnimator.mAnimationsEnded).isTrue();
-    }
-
-    private static class StreamItemAnimatorForTest extends StreamItemAnimator {
-        private boolean mAnimationsEnded;
-
-        StreamItemAnimatorForTest(
-                ContentChangedListener contentChangedListener, ViewActionManager actionManager) {
-            super(contentChangedListener, actionManager, null);
-        }
-
-        @Override
-        public void endAnimations() {
-            super.endAnimations();
-            mAnimationsEnded = true;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacksTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacksTest.java
deleted file mode 100644
index c8a1f93..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacksTest.java
+++ /dev/null
@@ -1,133 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.widget.FrameLayout;
-
-import androidx.recyclerview.widget.ItemTouchHelper;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.SwipeableViewHolder;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link StreamItemTouchCallbacks}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class StreamItemTouchCallbacksTest {
-    private final int mNoMovementFlag = 0;
-
-    private DismissibleViewHolder mDismissibleViewHolder;
-
-    private StreamItemTouchCallbacks mCallbacks;
-    private FrameLayout mFrameLayout;
-    private RecyclerView mRecyclerView;
-
-    @Before
-    public void setUp() {
-        mCallbacks = new StreamItemTouchCallbacks();
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        mRecyclerView = new RecyclerView(context);
-        mFrameLayout = new FrameLayout(context);
-        mDismissibleViewHolder =
-                new DismissibleViewHolder(mFrameLayout, /* isDismissible= */ false);
-    }
-
-    @Test
-    public void testGetMovementFlags_nonSwipeableViewHolderType() {
-        ViewHolder nonPietViewHolder = mock(ViewHolder.class);
-
-        int movementFlags = mCallbacks.getMovementFlags(mRecyclerView, nonPietViewHolder);
-
-        assertThat(movementFlags).isEqualTo(mNoMovementFlag);
-    }
-
-    @Test
-    public void testGetMovementFlags_notDismissibleSwipeableViewholder() {
-        mDismissibleViewHolder =
-                new DismissibleViewHolder(mFrameLayout, /* isDismissible= */ false);
-
-        int movementFlags = mCallbacks.getMovementFlags(mRecyclerView, mDismissibleViewHolder);
-
-        assertThat(movementFlags).isEqualTo(mNoMovementFlag);
-    }
-
-    @Test
-    public void testGetMovementFlags_swipeablePiet() {
-        mDismissibleViewHolder = new DismissibleViewHolder(mFrameLayout, /* isDismissible= */ true);
-
-        int movementFlags = mCallbacks.getMovementFlags(mRecyclerView, mDismissibleViewHolder);
-
-        assertThat(movementFlags)
-                .isEqualTo(ItemTouchHelper.Callback.makeMovementFlags(
-                        0, ItemTouchHelper.START | ItemTouchHelper.END));
-    }
-
-    @Test
-    public void testOnSwiped() {
-        mDismissibleViewHolder = new DismissibleViewHolder(mFrameLayout, /* isDismissible= */ true);
-        assertThat(mDismissibleViewHolder.isOnSwipedCalled()).isFalse();
-
-        mCallbacks.onSwiped(mDismissibleViewHolder, 0);
-
-        assertThat(mDismissibleViewHolder.isOnSwipedCalled()).isTrue();
-    }
-
-    @Test
-    public void testOnChildDraw() {
-        float translationX = 65;
-        mFrameLayout.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY));
-        mDismissibleViewHolder =
-                new DismissibleViewHolder(mFrameLayout, /* isDismissible= */ false);
-
-        mCallbacks.onChildDraw(new Canvas(), mRecyclerView, mDismissibleViewHolder, translationX,
-                /* dY= */ 0,
-                /* i= */ 1,
-                /* isCurrentlyActive= */ true);
-
-        assertThat(mDismissibleViewHolder.itemView.getTranslationX()).isEqualTo(translationX);
-        assertThat(mDismissibleViewHolder.itemView.getAlpha()).isEqualTo(.5f);
-    }
-
-    private static class DismissibleViewHolder extends ViewHolder implements SwipeableViewHolder {
-        private final boolean mIsDismissible;
-        private boolean mOnSwipedCalled;
-
-        DismissibleViewHolder(View view, boolean isDismissible) {
-            super(view);
-            this.mIsDismissible = isDismissible;
-        }
-
-        @Override
-        public boolean canSwipe() {
-            return mIsDismissible;
-        }
-
-        @Override
-        public void onSwiped() {
-            mOnSwipedCalled = true;
-        }
-
-        boolean isOnSwipedCalled() {
-            return mOnSwipedCalled;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamRecyclerViewAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamRecyclerViewAdapterTest.java
deleted file mode 100644
index ee1cab06..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamRecyclerViewAdapterTest.java
+++ /dev/null
@@ -1,560 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.LinearLayout;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.ContentDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.HeaderDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.LeafFeatureDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.StreamDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.ContinuationViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.FeedViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.NoContentViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.PietViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.ViewHolderType;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.ZeroStateViewHolder;
-import org.chromium.chrome.browser.feed.library.piet.FrameAdapter;
-import org.chromium.chrome.browser.feed.library.piet.PietManager;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.EventLogger;
-import org.chromium.chrome.browser.feed.library.sharedstream.deepestcontenttracker.DeepestContentTracker;
-import org.chromium.chrome.browser.feed.library.sharedstream.piet.PietEventLogger;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObservable;
-import org.chromium.chrome.browser.feed.shared.stream.Header;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link StreamRecyclerViewAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamRecyclerViewAdapterTest {
-    private static final int HEADER_COUNT = 2;
-    private static final long FEATURE_DRIVER_1_ID = 123;
-    private static final long FEATURE_DRIVER_2_ID = 321;
-    private static final String INITIAL_CONTENT_ID = "INITIAL_CONTENT_ID";
-    private static final String FEATURE_DRIVER_1_CONTENT_ID = "FEATURE_DRIVER_1_CONTENT_ID";
-    private static final String FEATURE_DRIVER_2_CONTENT_ID = "FEATURE_DRIVER_2_CONTENT_ID";
-    private static final Configuration CONFIGURATION = new Configuration.Builder().build();
-
-    @Mock
-    private CardConfiguration mCardConfiguration;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private PietManager mPietManager;
-    @Mock
-    private FrameAdapter mFrameAdapter;
-    @Mock
-    private StreamDriver mDriver;
-    @Mock
-    private ContentDriver mInitialFeatureDriver;
-    @Mock
-    private ContentDriver mFeatureDriver1;
-    @Mock
-    private ContentDriver mFeatureDriver2;
-    @Mock
-    private HeaderDriver mHeaderDriver1;
-    @Mock
-    private HeaderDriver mHeaderDriver2;
-    @Mock
-    private DeepestContentTracker mDeepestContentTracker;
-    @Mock
-    private Header mHeader1;
-    @Mock
-    private Header mHeader2;
-    @Mock
-    private ScrollObservable mScrollObservable;
-    @Mock
-    private PietEventLogger mPietEventLogger;
-
-    private Context mContext;
-    private LinearLayout mFrameContainer;
-    private StreamRecyclerViewAdapter mStreamRecyclerViewAdapter;
-    private List<Header> mHeaders;
-    private StreamAdapterObserver mStreamAdapterObserver;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mContext.setTheme(R.style.Light);
-        mFrameContainer = new LinearLayout(mContext);
-
-        when(mPietManager.createPietFrameAdapter(ArgumentMatchers.<Supplier<ViewGroup>>any(),
-                     any(ActionHandler.class), any(EventLogger.class), eq(mContext)))
-                .thenReturn(mFrameAdapter);
-        when(mFrameAdapter.getFrameContainer()).thenReturn(mFrameContainer);
-
-        when(mFeatureDriver1.itemId()).thenReturn(FEATURE_DRIVER_1_ID);
-        when(mFeatureDriver2.itemId()).thenReturn(FEATURE_DRIVER_2_ID);
-        when(mInitialFeatureDriver.getContentId()).thenReturn(INITIAL_CONTENT_ID);
-
-        mHeaders = ImmutableList.of(mHeader1, mHeader2);
-
-        when(mFeatureDriver1.getContentId()).thenReturn(FEATURE_DRIVER_1_CONTENT_ID);
-        when(mFeatureDriver2.getContentId()).thenReturn(FEATURE_DRIVER_2_CONTENT_ID);
-        when(mHeaderDriver1.getHeader()).thenReturn(mHeader1);
-        when(mHeaderDriver2.getHeader()).thenReturn(mHeader2);
-
-        mStreamRecyclerViewAdapter = new StreamRecyclerViewAdapter(mContext,
-                new RecyclerView(mContext), mCardConfiguration, mPietManager,
-                mDeepestContentTracker, mContentChangedListener, mScrollObservable, CONFIGURATION,
-                mPietEventLogger);
-        mStreamRecyclerViewAdapter.setHeaders(mHeaders);
-
-        when(mDriver.getLeafFeatureDrivers()).thenReturn(Lists.newArrayList(mInitialFeatureDriver));
-        mStreamRecyclerViewAdapter.setDriver(mDriver);
-
-        mStreamAdapterObserver = new StreamAdapterObserver();
-        mStreamRecyclerViewAdapter.registerAdapterDataObserver(mStreamAdapterObserver);
-    }
-
-    @Test
-    public void testCreateViewHolderPiet() {
-        FrameLayout parent = new FrameLayout(mContext);
-        ViewHolder viewHolder =
-                mStreamRecyclerViewAdapter.onCreateViewHolder(parent, ViewHolderType.TYPE_CARD);
-
-        FrameLayout cardView = getCardView(viewHolder);
-        assertThat(cardView.getChildAt(0)).isEqualTo(mFrameContainer);
-    }
-
-    @Test
-    public void testCreateViewHolderContinuation() {
-        FrameLayout parent = new FrameLayout(mContext);
-        ViewHolder viewHolder = mStreamRecyclerViewAdapter.onCreateViewHolder(
-                parent, ViewHolderType.TYPE_CONTINUATION);
-        FrameLayout viewHolderFrameLayout = getCardView(viewHolder);
-
-        assertThat(viewHolder).isInstanceOf(ContinuationViewHolder.class);
-        assertThat(viewHolderFrameLayout.getLayoutParams().height)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(viewHolderFrameLayout.getLayoutParams().width)
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-    }
-
-    @Test
-    public void testCreateViewHolderNoContent() {
-        FrameLayout parent = new FrameLayout(mContext);
-        ViewHolder viewHolder = mStreamRecyclerViewAdapter.onCreateViewHolder(
-                parent, ViewHolderType.TYPE_NO_CONTENT);
-        FrameLayout viewHolderFrameLayout = getCardView(viewHolder);
-
-        assertThat(viewHolder).isInstanceOf(NoContentViewHolder.class);
-        assertThat(viewHolderFrameLayout.getLayoutParams().height)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(viewHolderFrameLayout.getLayoutParams().width)
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-    }
-
-    @Test
-    public void testCreateViewHolderZeroState() {
-        FrameLayout parent = new FrameLayout(mContext);
-        ViewHolder viewHolder = mStreamRecyclerViewAdapter.onCreateViewHolder(
-                parent, ViewHolderType.TYPE_ZERO_STATE);
-        FrameLayout viewHolderFrameLayout = getCardView(viewHolder);
-
-        assertThat(viewHolder).isInstanceOf(ZeroStateViewHolder.class);
-        assertThat(viewHolderFrameLayout.getLayoutParams().height)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(viewHolderFrameLayout.getLayoutParams().width)
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-    }
-
-    @Test
-    public void testOnBindViewHolder() {
-        FrameLayout parent = new FrameLayout(mContext);
-        FeedViewHolder viewHolder =
-                mStreamRecyclerViewAdapter.onCreateViewHolder(parent, ViewHolderType.TYPE_CARD);
-
-        mStreamRecyclerViewAdapter.onBindViewHolder(viewHolder, getContentBindingIndex(0));
-
-        verify(mInitialFeatureDriver).bind(viewHolder);
-    }
-
-    @Test
-    public void testOnViewRecycled() {
-        PietViewHolder viewHolder = mock(PietViewHolder.class);
-
-        mStreamRecyclerViewAdapter.onBindViewHolder(viewHolder, getContentBindingIndex(0));
-
-        // Make sure the content model is bound
-        verify(mInitialFeatureDriver).bind(viewHolder);
-
-        mStreamRecyclerViewAdapter.onViewRecycled(viewHolder);
-
-        verify(mInitialFeatureDriver).unbind();
-    }
-
-    @Test
-    public void testSetDriver_initialContentModels() {
-        // streamRecyclerViewAdapter.setDriver(driver) is called in setup()
-        assertThat(mStreamRecyclerViewAdapter.getLeafFeatureDrivers())
-                .containsExactly(mInitialFeatureDriver);
-    }
-
-    @Test
-    public void testSetDriver_newDriver() {
-        StreamDriver newDriver = mock(StreamDriver.class);
-        List<LeafFeatureDriver> newFeatureDrivers =
-                Lists.newArrayList(mFeatureDriver1, mFeatureDriver2);
-
-        when(newDriver.getLeafFeatureDrivers()).thenReturn(newFeatureDrivers);
-
-        mStreamRecyclerViewAdapter.setDriver(newDriver);
-
-        verify(mDriver).setStreamContentListener(null);
-        assertThat(mStreamRecyclerViewAdapter.getItemId(getContentBindingIndex(0)))
-                .isEqualTo(FEATURE_DRIVER_1_ID);
-        assertThat(mStreamRecyclerViewAdapter.getItemId(getContentBindingIndex(1)))
-                .isEqualTo(FEATURE_DRIVER_2_ID);
-
-        InOrder inOrder = Mockito.inOrder(newDriver);
-
-        // getLeafFeatureDrivers() needs to be called before setting the listener, otherwise the
-        // adapter could be notified of a child and then given it in getLeafFeatureDrivers().
-        inOrder.verify(newDriver).getLeafFeatureDrivers();
-        inOrder.verify(newDriver).setStreamContentListener(mStreamRecyclerViewAdapter);
-        inOrder.verify(newDriver).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testNotifyContentsAdded_endOfList() {
-        mStreamRecyclerViewAdapter.notifyContentsAdded(
-                1, Lists.newArrayList(mFeatureDriver1, mFeatureDriver2));
-        assertThat(mStreamRecyclerViewAdapter.getLeafFeatureDrivers())
-                .containsAtLeast(mInitialFeatureDriver, mFeatureDriver1, mFeatureDriver2);
-    }
-
-    @Test
-    public void testNotifyContentsAdded_startOfList() {
-        mStreamRecyclerViewAdapter.notifyContentsAdded(
-                0, Lists.newArrayList(mFeatureDriver1, mFeatureDriver2));
-        assertThat(mStreamRecyclerViewAdapter.getLeafFeatureDrivers())
-                .containsAtLeast(mFeatureDriver1, mFeatureDriver2, mInitialFeatureDriver);
-    }
-
-    @Test
-    public void testNotifyContentsRemoved() {
-        mStreamRecyclerViewAdapter.notifyContentRemoved(0);
-        assertThat(mStreamRecyclerViewAdapter.getLeafFeatureDrivers()).isEmpty();
-        verify(mDeepestContentTracker).removeContentId(0);
-    }
-
-    @Test
-    public void testNotifyContentsAdded_notifiesListener() {
-        mStreamRecyclerViewAdapter.setShown(false);
-        mStreamRecyclerViewAdapter.notifyContentsAdded(
-                0, Lists.newArrayList(mFeatureDriver1, mFeatureDriver2));
-
-        assertThat(mStreamAdapterObserver.mInsertedStart).isEqualTo(mHeaders.size());
-        assertThat(mStreamAdapterObserver.mInsertedCount).isEqualTo(2);
-        verify(mContentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsAdded_doesNotNotifiesContentChangedListener_ifShownTrue() {
-        mStreamRecyclerViewAdapter.setShown(true);
-        mStreamRecyclerViewAdapter.notifyContentsAdded(
-                0, Lists.newArrayList(mFeatureDriver1, mFeatureDriver2));
-
-        verify(mContentChangedListener, never()).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsAdded_streamVisibleFalse() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        mStreamAdapterObserver.reset();
-
-        mStreamRecyclerViewAdapter.notifyContentsAdded(
-                0, Lists.newArrayList(mFeatureDriver1, mFeatureDriver2));
-        assertThat(mStreamAdapterObserver.mInsertedStart).isEqualTo(0);
-        assertThat(mStreamAdapterObserver.mInsertedCount).isEqualTo(0);
-    }
-
-    @Test
-    public void testNotifyContentsRemoved_notifiesListener() {
-        mStreamRecyclerViewAdapter.setShown(false);
-        mStreamRecyclerViewAdapter.notifyContentRemoved(0);
-
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(mHeaders.size());
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(1);
-        verify(mContentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsRemoved_doesNotNotifiesContentChangedListener_ifShownTrue() {
-        mStreamRecyclerViewAdapter.setShown(true);
-        mStreamRecyclerViewAdapter.notifyContentRemoved(0);
-
-        verify(mContentChangedListener, never()).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsRemoved_streamVisibleFalse() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        mStreamAdapterObserver.reset();
-
-        mStreamRecyclerViewAdapter.notifyContentRemoved(0);
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(0);
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(0);
-    }
-
-    @Test
-    public void testNotifyContentsCleared_notifiesListener() {
-        mStreamRecyclerViewAdapter.setShown(false);
-        mStreamRecyclerViewAdapter.notifyContentsCleared();
-
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(mHeaders.size());
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(1);
-        verify(mContentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsCleared_doesNotNotifiesContentChangedListener_ifShownTrue() {
-        mStreamRecyclerViewAdapter.setShown(true);
-        mStreamRecyclerViewAdapter.notifyContentsCleared();
-
-        verify(mContentChangedListener, never()).onContentChanged();
-    }
-
-    @Test
-    public void testNotifyContentsCleared_streamVisibleFalse() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        mStreamAdapterObserver.reset();
-
-        mStreamRecyclerViewAdapter.notifyContentsCleared();
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(0);
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(0);
-    }
-
-    @Test
-    public void testOnDestroy() {
-        mStreamRecyclerViewAdapter = new StreamRecyclerViewAdapter(mContext,
-                new RecyclerView(mContext), mCardConfiguration, mPietManager,
-                mDeepestContentTracker, mContentChangedListener, mScrollObservable, CONFIGURATION,
-                mPietEventLogger) {
-            @Override
-            HeaderDriver createHeaderDriver(Header header) {
-                if (header == mHeaders.get(0)) {
-                    return mHeaderDriver1;
-                }
-                return mHeaderDriver2;
-            }
-        };
-        mStreamRecyclerViewAdapter.setHeaders(mHeaders);
-        mStreamRecyclerViewAdapter.onDestroy();
-
-        verify(mHeaderDriver1).unbind();
-        verify(mHeaderDriver1).onDestroy();
-        verify(mHeaderDriver2).unbind();
-        verify(mHeaderDriver2).onDestroy();
-    }
-
-    @Test
-    public void testSetHeaders_destroysPreviousHeaderDrivers() {
-        mStreamRecyclerViewAdapter = new StreamRecyclerViewAdapter(mContext,
-                new RecyclerView(mContext), mCardConfiguration, mPietManager,
-                mDeepestContentTracker, mContentChangedListener, mScrollObservable, CONFIGURATION,
-                mPietEventLogger) {
-            @Override
-            HeaderDriver createHeaderDriver(Header header) {
-                if (header == mHeaders.get(0)) {
-                    return mHeaderDriver1;
-                }
-                return mHeaderDriver2;
-            }
-        };
-        mStreamRecyclerViewAdapter.setHeaders(mHeaders);
-
-        mStreamRecyclerViewAdapter.setHeaders(Collections.emptyList());
-
-        verify(mHeaderDriver1).onDestroy();
-        verify(mHeaderDriver2).onDestroy();
-    }
-
-    @Test
-    public void testGetItemCount() {
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(mHeaders.size() + 1);
-    }
-
-    @Test
-    public void testGetItemCount_streamVisibleFalse() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(mHeaders.size());
-    }
-
-    @Test
-    public void setStreamContentVisible_hidesContent() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(mHeaders.size());
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(1);
-    }
-
-    @Test
-    public void setStreamContentVisible_hidesContent_doubleCall() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-
-        mStreamAdapterObserver.reset();
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-
-        // Nothing additional should get called
-        assertThat(mStreamAdapterObserver.mRemovedStart).isEqualTo(0);
-        assertThat(mStreamAdapterObserver.mRemovedCount).isEqualTo(0);
-    }
-
-    @Test
-    public void setStreamContentVisible_showsContent() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        mStreamAdapterObserver.reset();
-
-        mStreamRecyclerViewAdapter.setStreamContentVisible(true);
-        assertThat(mStreamAdapterObserver.mInsertedStart).isEqualTo(mHeaders.size());
-        assertThat(mStreamAdapterObserver.mInsertedCount).isEqualTo(1);
-    }
-
-    @Test
-    public void setStreamContentVisible_showsContent_doubleCall() {
-        mStreamRecyclerViewAdapter.setStreamContentVisible(false);
-        mStreamAdapterObserver.reset();
-
-        mStreamRecyclerViewAdapter.setStreamContentVisible(true);
-
-        mStreamAdapterObserver.reset();
-        mStreamRecyclerViewAdapter.setStreamContentVisible(true);
-
-        // Nothing additional should get called
-        assertThat(mStreamAdapterObserver.mInsertedStart).isEqualTo(0);
-        assertThat(mStreamAdapterObserver.mInsertedCount).isEqualTo(0);
-    }
-
-    @Test
-    public void testOnBindViewHolder_updatesDeepestViewedContent() {
-        PietViewHolder viewHolder = mock(PietViewHolder.class);
-
-        mStreamRecyclerViewAdapter.onBindViewHolder(viewHolder, getContentBindingIndex(0));
-
-        verify(mDeepestContentTracker).updateDeepestContentTracker(0, INITIAL_CONTENT_ID);
-    }
-
-    @Test
-    public void testDismissHeader() {
-        mStreamRecyclerViewAdapter = new StreamRecyclerViewAdapter(mContext,
-                new RecyclerView(mContext), mCardConfiguration, mPietManager,
-                mDeepestContentTracker, mContentChangedListener, mScrollObservable, CONFIGURATION,
-                mPietEventLogger) {
-            @Override
-            HeaderDriver createHeaderDriver(Header header) {
-                if (header == mHeaders.get(0)) {
-                    return mHeaderDriver1;
-                }
-                return mHeaderDriver2;
-            }
-        };
-        mStreamRecyclerViewAdapter.setHeaders(mHeaders);
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(2);
-
-        mStreamRecyclerViewAdapter.dismissHeader(mHeader1);
-
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(1);
-        verify(mHeader1).onDismissed();
-        verify(mHeaderDriver1).onDestroy();
-    }
-
-    @Test
-    public void testDismissHeader_whenNoHeaders() {
-        mStreamRecyclerViewAdapter.setHeaders(Collections.emptyList());
-        reset(mHeader1);
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(1);
-
-        mStreamRecyclerViewAdapter.dismissHeader(mHeader1);
-
-        assertThat(mStreamRecyclerViewAdapter.getItemCount()).isEqualTo(1);
-        verify(mHeader1, never()).onDismissed();
-    }
-
-    @Test
-    public void testRebind() {
-        PietViewHolder viewHolder = mock(PietViewHolder.class);
-        mStreamRecyclerViewAdapter.onBindViewHolder(viewHolder, getContentBindingIndex(0));
-        mStreamRecyclerViewAdapter.rebind();
-        verify(mInitialFeatureDriver).maybeRebind();
-    }
-
-    private FrameLayout getCardView(ViewHolder viewHolder) {
-        return (FrameLayout) viewHolder.itemView;
-    }
-
-    private int getContentBindingIndex(int index) {
-        return HEADER_COUNT + index;
-    }
-
-    private class StreamAdapterObserver extends RecyclerView.AdapterDataObserver {
-        private int mInsertedStart;
-        private int mInsertedCount;
-        private int mRemovedStart;
-        private int mRemovedCount;
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            mInsertedStart = positionStart;
-            mInsertedCount = itemCount;
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            mRemovedStart = positionStart;
-            mRemovedCount = itemCount;
-        }
-
-        public void reset() {
-            mInsertedStart = 0;
-            mInsertedCount = 0;
-            mRemovedStart = 0;
-            mRemovedCount = 0;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/actions/StreamActionApiImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/actions/StreamActionApiImplTest.java
deleted file mode 100644
index 732f444b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/actions/StreamActionApiImplTest.java
+++ /dev/null
@@ -1,729 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.actions;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentMetadata;
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ActionType;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ContentLoggingData;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipCallbackApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipCallbackApi.TooltipDismissType;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipInfo;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParser;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionSource;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.pendingdismiss.ClusterPendingDismissHelper;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.StreamContentLoggingData;
-import org.chromium.chrome.browser.feed.library.sharedstream.pendingdismiss.PendingDismissCallback;
-import org.chromium.chrome.browser.feed.library.testing.sharedstream.contextmenumanager.FakeContextMenuManager;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionPayloadProto.FeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.ElementType;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.LabelledFeedActionData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.OpenContextMenuData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.OpenUrlData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.UndoAction;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.BasicLoggingMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.RepresentationData;
-import org.chromium.components.feed.core.proto.wire.ActionPayloadProto.ActionPayload;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link StreamActionApiImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-@Features.DisableFeatures(ChromeFeatureList.INTEREST_FEED_V2)
-@Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)
-public class StreamActionApiImplTest {
-    private static final String URL = "www.google.com";
-    private static final String OPEN_LABEL = "Open";
-    private static final String OPEN_IN_NEW_WINDOW_LABEL = "Open in new window";
-    private static final String PARAM = "param";
-    private static final String NEW_URL = "ooh.shiny.com";
-    private static final int INTEREST_TYPE = 2;
-    private static final ActionPayload ACTION_PAYLOAD = ActionPayload.getDefaultInstance();
-    private static final LabelledFeedActionData OPEN_IN_NEW_WINDOW =
-            LabelledFeedActionData.newBuilder()
-                    .setLabel(OPEN_IN_NEW_WINDOW_LABEL)
-                    .setFeedActionPayload(FeedActionPayload.newBuilder().setExtension(
-                            FeedAction.feedActionExtension,
-                            FeedAction.newBuilder()
-                                    .setMetadata(
-                                            FeedActionMetadata.newBuilder()
-                                                    .setType(Type.OPEN_URL_NEW_WINDOW)
-                                                    .setOpenUrlData(
-                                                            OpenUrlData.newBuilder().setUrl(URL)))
-                                    .build()))
-                    .build();
-
-    private static final String OPEN_IN_INCOGNITO_MODE_LABEL = "Open in incognito mode";
-
-    private static final LabelledFeedActionData OPEN_IN_INCOGNITO_MODE =
-            LabelledFeedActionData.newBuilder()
-                    .setLabel(OPEN_IN_INCOGNITO_MODE_LABEL)
-                    .setFeedActionPayload(FeedActionPayload.newBuilder().setExtension(
-                            FeedAction.feedActionExtension,
-                            FeedAction.newBuilder()
-                                    .setMetadata(
-                                            FeedActionMetadata.newBuilder()
-                                                    .setType(Type.OPEN_URL_INCOGNITO)
-                                                    .setOpenUrlData(
-                                                            OpenUrlData.newBuilder().setUrl(URL)))
-                                    .build()))
-                    .build();
-
-    private static final LabelledFeedActionData NORMAL_OPEN_URL =
-            LabelledFeedActionData.newBuilder()
-                    .setLabel(OPEN_LABEL)
-                    .setFeedActionPayload(FeedActionPayload.newBuilder().setExtension(
-                            FeedAction.feedActionExtension,
-                            FeedAction.newBuilder()
-                                    .setMetadata(
-                                            FeedActionMetadata.newBuilder()
-                                                    .setType(Type.OPEN_URL)
-                                                    .setOpenUrlData(
-                                                            OpenUrlData.newBuilder().setUrl(URL)))
-                                    .build()))
-                    .build();
-    private static final String SESSION_ID = "SESSION_ID";
-    private static final String CONTENT_ID = "CONTENT_ID";
-    private static final ContentMetadata CONTENT_METADATA = new ContentMetadata(URL, "title",
-            /* timePublished= */ -1,
-            /* imageUrl= */ null,
-            /* publisher= */ null,
-            /* faviconUrl= */ null,
-            /* snippet=*/null);
-
-    @Mock
-    private ActionApi mActionApi;
-    @Mock
-    private ActionParser mActionParser;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private ClusterPendingDismissHelper mClusterPendingDismissHelper;
-    @Mock
-    private ViewElementActionHandler mViewElementActionHandler;
-    @Mock
-    private TooltipApi mTooltipApi;
-
-    @Captor
-    private ArgumentCaptor<Consumer<String>> mConsumerCaptor;
-    private ContentLoggingData mContentLoggingData;
-    private FakeContextMenuManager mContextMenuManager;
-    private StreamActionApiImpl mStreamActionApi;
-    private View mView;
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Before
-    public void setup() {
-        initMocks(this);
-
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        mContentLoggingData = new StreamContentLoggingData(0,
-                BasicLoggingMetadata.getDefaultInstance(), RepresentationData.getDefaultInstance(),
-                /* availableOffline= */ false);
-        mView = new View(context);
-        mContextMenuManager = new FakeContextMenuManager();
-        mStreamActionApi =
-                new StreamActionApiImpl(mActionApi, mActionParser, mActionManager, mBasicLoggingApi,
-                        ()
-                                -> mContentLoggingData,
-                        mContextMenuManager, SESSION_ID, mClusterPendingDismissHelper,
-                        mViewElementActionHandler, CONTENT_ID, mTooltipApi);
-    }
-
-    @Test
-    public void testCanDismiss() {
-        assertThat(mStreamActionApi.canDismiss()).isTrue();
-    }
-
-    @Test
-    public void testDismiss() {
-        String contentId = "contentId";
-        List<StreamDataOperation> streamDataOperations =
-                Collections.singletonList(StreamDataOperation.getDefaultInstance());
-        mStreamActionApi.dismiss(
-                contentId, streamDataOperations, UndoAction.getDefaultInstance(), ACTION_PAYLOAD);
-
-        verify(mActionManager)
-                .dismissLocal(ImmutableList.of(contentId), streamDataOperations, SESSION_ID);
-        verify(mBasicLoggingApi).onContentDismissed(mContentLoggingData, /*wasCommitted =*/true);
-    }
-
-    @Test
-    public void testDismiss_withSnackbar_onCommitted() {
-        testCommittedDismissWithSnackbar(Type.DISMISS);
-    }
-
-    @Test
-    public void testDismiss_withSnackbar_onReverted() {
-        testRevertedDismissWithSnackbar(Type.DISMISS);
-    }
-
-    @Test
-    public void testDismiss_noSnackbar() {
-        testDismissNoSnackbar(Type.DISMISS);
-    }
-
-    @Test
-    public void testDismissWithSnackbar_dismissLocal_onCommitted() {
-        testCommittedDismissWithSnackbar(Type.DISMISS_LOCAL);
-    }
-
-    @Test
-    public void testDismissWithSnackbar_dismissLocal_onReverted() {
-        testRevertedDismissWithSnackbar(Type.DISMISS_LOCAL);
-    }
-
-    @Test
-    public void testDismissNoSnackbar_dismissLocal_onReverted() {
-        testDismissNoSnackbar(Type.DISMISS_LOCAL);
-    }
-
-    @Test
-    public void testHandleNotInterestedInTopic_onCommitted() {
-        testCommittedDismissWithSnackbar(Type.NOT_INTERESTED_IN);
-    }
-
-    @Test
-    public void testHandleNotInterestedInTopic_onReverted() {
-        testRevertedDismissWithSnackbar(Type.NOT_INTERESTED_IN);
-    }
-
-    @Test
-    public void testHandleNotInterestedInTopic_noSnackbar() {
-        testDismissNoSnackbar(Type.NOT_INTERESTED_IN);
-    }
-
-    @Test
-    public void testHandleBlockContent() {
-        List<StreamDataOperation> streamDataOperations =
-                Collections.singletonList(StreamDataOperation.getDefaultInstance());
-        mStreamActionApi.handleBlockContent(streamDataOperations, ACTION_PAYLOAD);
-
-        verify(mActionManager).dismiss(streamDataOperations, SESSION_ID);
-        verify(mActionManager)
-                .createAndUploadAction(
-                        CONTENT_ID, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-    }
-
-    @Test
-    public void testOnClientAction() {
-        mStreamActionApi.onClientAction(ActionType.OPEN_URL);
-
-        verify(mBasicLoggingApi).onClientAction(mContentLoggingData, ActionType.OPEN_URL);
-    }
-
-    @Test
-    public void testOnElementClick_logsElementClicked() {
-        mStreamActionApi.onElementClick(ElementType.INTEREST_HEADER.getNumber());
-
-        verify(mBasicLoggingApi)
-                .onVisualElementClicked(
-                        mContentLoggingData, ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOnElementClick_logsElementClicked_retrievesLoggingDataLazily() {
-        mContentLoggingData = new StreamContentLoggingData(1,
-                BasicLoggingMetadata.getDefaultInstance(), RepresentationData.getDefaultInstance(),
-                /* availableOffline= */ true);
-
-        mStreamActionApi.onElementClick(ElementType.INTEREST_HEADER.getNumber());
-
-        // Should use the new ContentLoggingData defined, not the old one.
-        verify(mBasicLoggingApi)
-                .onVisualElementClicked(
-                        mContentLoggingData, ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOnElementView() {
-        mStreamActionApi.onElementView(ElementType.INTEREST_HEADER.getNumber());
-
-        verify(mViewElementActionHandler).onElementView(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testReportClickAction_withFeature() {
-        String contentId = "contentId";
-        mStreamActionApi.reportClickAction(contentId, ACTION_PAYLOAD);
-
-        verify(mActionManager)
-                .createAndUploadAction(
-                        contentId, ACTION_PAYLOAD, ActionManager.UploadActionType.CLICK);
-    }
-
-    @Test
-    @Features.DisableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testNoReportClickAction_withoutFeature() {
-        mStreamActionApi.reportClickAction("contentId", ACTION_PAYLOAD);
-
-        verify(mActionManager, never())
-                .createAndUploadAction(anyString(), any(ActionPayload.class),
-                        any(ActionManager.UploadActionType.class));
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testReportViewVisible_withFeature() {
-        String contentId = "contentId";
-        mStreamActionApi.reportViewVisible(mView, contentId, ACTION_PAYLOAD);
-
-        verify(mActionManager).onViewVisible(mView, contentId, ACTION_PAYLOAD);
-    }
-
-    @Test
-    @Features.DisableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testReportViewVisible_withoutFeature() {
-        mStreamActionApi.reportViewHidden(mView, "contentId");
-
-        verify(mActionManager, never())
-                .onViewVisible(any(View.class), anyString(), any(ActionPayload.class));
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testReportViewHidden_withFeature() {
-        String contentId = "contentId";
-        mStreamActionApi.reportViewHidden(mView, contentId);
-
-        verify(mActionManager).onViewHidden(mView, contentId);
-    }
-
-    @Test
-    @Features.DisableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testReportViewHidden_withoutFeature() {
-        mStreamActionApi.reportViewHidden(mView, "contentId");
-
-        verify(mActionManager, never()).onViewHidden(any(View.class), anyString());
-    }
-
-    @Test
-    public void testOnElementHide() {
-        mStreamActionApi.onElementHide(ElementType.INTEREST_HEADER.getNumber());
-
-        verify(mViewElementActionHandler).onElementHide(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOpenUrl() {
-        mStreamActionApi.openUrlInNewWindow(URL);
-        verify(mActionApi).openUrlInNewWindow(URL);
-    }
-
-    @Test
-    public void testOpenUrl_withParam() {
-        mStreamActionApi.openUrl(URL, PARAM);
-
-        verify(mActionManager)
-                .uploadAllActionsAndUpdateUrl(eq(URL), eq(PARAM), mConsumerCaptor.capture());
-        mConsumerCaptor.getValue().accept(NEW_URL);
-        verify(mActionApi).openUrl(NEW_URL);
-    }
-
-    @Test
-    public void testCanOpenUrl() {
-        when(mActionApi.canOpenUrl()).thenReturn(true);
-        assertThat(mStreamActionApi.canOpenUrl()).isTrue();
-
-        when(mActionApi.canOpenUrl()).thenReturn(false);
-        assertThat(mStreamActionApi.canOpenUrl()).isFalse();
-    }
-
-    @Test
-    public void testCanOpenUrlInIncognitoMode() {
-        when(mActionApi.canOpenUrlInIncognitoMode()).thenReturn(true);
-        assertThat(mStreamActionApi.canOpenUrlInIncognitoMode()).isTrue();
-
-        when(mActionApi.canOpenUrlInIncognitoMode()).thenReturn(false);
-        assertThat(mStreamActionApi.canOpenUrlInIncognitoMode()).isFalse();
-    }
-
-    @Test
-    public void testOpenUrlInIncognitoMode_withParam() {
-        mStreamActionApi.openUrlInIncognitoMode(URL, PARAM);
-
-        verify(mActionManager)
-                .uploadAllActionsAndUpdateUrl(eq(URL), eq(PARAM), mConsumerCaptor.capture());
-        mConsumerCaptor.getValue().accept(NEW_URL);
-        verify(mActionApi).openUrlInIncognitoMode(NEW_URL);
-    }
-
-    @Test
-    public void testOpenUrlInNewTab() {
-        mStreamActionApi.openUrlInNewTab(URL);
-        verify(mActionApi).openUrlInNewTab(URL);
-    }
-
-    @Test
-    public void testOpenUrlInNewTab_withParam() {
-        mStreamActionApi.openUrlInNewTab(URL, PARAM);
-
-        verify(mActionManager)
-                .uploadAllActionsAndUpdateUrl(eq(URL), eq(PARAM), mConsumerCaptor.capture());
-        mConsumerCaptor.getValue().accept(NEW_URL);
-        verify(mActionApi).openUrlInNewTab(NEW_URL);
-    }
-
-    @Test
-    public void testCanOpenUrlInNewTab() {
-        when(mActionApi.canOpenUrlInNewTab()).thenReturn(true);
-        assertThat(mStreamActionApi.canOpenUrlInNewTab()).isTrue();
-
-        when(mActionApi.canOpenUrlInNewTab()).thenReturn(false);
-        assertThat(mStreamActionApi.canOpenUrlInNewTab()).isFalse();
-    }
-
-    @Test
-    public void testDownloadUrl() {
-        mStreamActionApi.downloadUrl(CONTENT_METADATA);
-        verify(mActionApi).downloadUrl(CONTENT_METADATA);
-    }
-
-    @Test
-    public void testCanDownloadUrl() {
-        when(mActionApi.canDownloadUrl()).thenReturn(true);
-        assertThat(mStreamActionApi.canDownloadUrl()).isTrue();
-
-        when(mActionApi.canDownloadUrl()).thenReturn(false);
-        assertThat(mStreamActionApi.canDownloadUrl()).isFalse();
-    }
-
-    @Test
-    public void testLearnMore() {
-        mStreamActionApi.learnMore();
-        verify(mActionApi).learnMore();
-    }
-
-    @Test
-    public void testCanLearnMore() {
-        when(mActionApi.canLearnMore()).thenReturn(true);
-        assertThat(mStreamActionApi.canLearnMore()).isTrue();
-
-        when(mActionApi.canLearnMore()).thenReturn(false);
-        assertThat(mStreamActionApi.canLearnMore()).isFalse();
-    }
-
-    @Test
-    public void openContextMenuTest() {
-        when(mActionParser.canPerformAction(any(FeedActionPayload.class), eq(mStreamActionApi)))
-                .thenReturn(true);
-
-        List<LabelledFeedActionData> labelledFeedActionDataList = new ArrayList<>();
-        labelledFeedActionDataList.add(NORMAL_OPEN_URL);
-        labelledFeedActionDataList.add(OPEN_IN_NEW_WINDOW);
-        labelledFeedActionDataList.add(OPEN_IN_INCOGNITO_MODE);
-
-        mStreamActionApi.openContextMenu(OpenContextMenuData.newBuilder()
-                                                 .addAllContextMenuData(labelledFeedActionDataList)
-                                                 .build(),
-                mView);
-
-        mContextMenuManager.performClick(0);
-        mContextMenuManager.performClick(1);
-        mContextMenuManager.performClick(2);
-
-        InOrder inOrder = Mockito.inOrder(mActionParser);
-
-        inOrder.verify(mActionParser)
-                .parseFeedActionPayload(NORMAL_OPEN_URL.getFeedActionPayload(), mStreamActionApi,
-                        mView, ActionSource.CONTEXT_MENU);
-        inOrder.verify(mActionParser)
-                .parseFeedActionPayload(OPEN_IN_NEW_WINDOW.getFeedActionPayload(), mStreamActionApi,
-                        mView, ActionSource.CONTEXT_MENU);
-        inOrder.verify(mActionParser)
-                .parseFeedActionPayload(OPEN_IN_INCOGNITO_MODE.getFeedActionPayload(),
-                        mStreamActionApi, mView, ActionSource.CONTEXT_MENU);
-    }
-
-    @Test
-    public void openContextMenuTest_noNewWindow() {
-        when(mActionParser.canPerformAction(
-                     NORMAL_OPEN_URL.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(true);
-        when(mActionParser.canPerformAction(
-                     OPEN_IN_INCOGNITO_MODE.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(true);
-        when(mActionParser.canPerformAction(
-                     OPEN_IN_NEW_WINDOW.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(false);
-
-        List<LabelledFeedActionData> labelledFeedActionDataList = new ArrayList<>();
-        labelledFeedActionDataList.add(NORMAL_OPEN_URL);
-        labelledFeedActionDataList.add(OPEN_IN_NEW_WINDOW);
-        labelledFeedActionDataList.add(OPEN_IN_INCOGNITO_MODE);
-
-        mStreamActionApi.openContextMenu(OpenContextMenuData.newBuilder()
-                                                 .addAllContextMenuData(labelledFeedActionDataList)
-                                                 .build(),
-                mView);
-
-        assertThat(mContextMenuManager.getMenuOptions())
-                .isEqualTo(Arrays.asList(OPEN_LABEL, OPEN_IN_INCOGNITO_MODE_LABEL));
-    }
-
-    @Test
-    public void openContextMenuTest_noIncognitoWindow() {
-        when(mActionParser.canPerformAction(
-                     NORMAL_OPEN_URL.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(true);
-        when(mActionParser.canPerformAction(
-                     OPEN_IN_NEW_WINDOW.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(true);
-        when(mActionParser.canPerformAction(
-                     OPEN_IN_INCOGNITO_MODE.getFeedActionPayload(), mStreamActionApi))
-                .thenReturn(false);
-
-        List<LabelledFeedActionData> labelledFeedActionDataList = new ArrayList<>();
-        labelledFeedActionDataList.add(NORMAL_OPEN_URL);
-        labelledFeedActionDataList.add(OPEN_IN_NEW_WINDOW);
-        labelledFeedActionDataList.add(OPEN_IN_INCOGNITO_MODE);
-
-        mStreamActionApi.openContextMenu(OpenContextMenuData.newBuilder()
-                                                 .addAllContextMenuData(labelledFeedActionDataList)
-                                                 .build(),
-                mView);
-
-        assertThat(mContextMenuManager.getMenuOptions())
-                .isEqualTo(Arrays.asList(OPEN_LABEL, OPEN_IN_NEW_WINDOW_LABEL));
-    }
-
-    @Test
-    public void openContextMenuTest_logsContentContextMenuOpened() {
-        when(mActionParser.canPerformAction(any(FeedActionPayload.class), eq(mStreamActionApi)))
-                .thenReturn(true);
-
-        mStreamActionApi.openContextMenu(
-                OpenContextMenuData.newBuilder()
-                        .addAllContextMenuData(Collections.singletonList(NORMAL_OPEN_URL))
-                        .build(),
-                mView);
-
-        // First context menu succeeds in opening and is logged.
-        verify(mBasicLoggingApi).onContentContextMenuOpened(mContentLoggingData);
-
-        reset(mBasicLoggingApi);
-
-        mStreamActionApi.openContextMenu(
-                OpenContextMenuData.newBuilder()
-                        .addAllContextMenuData(Collections.singletonList(NORMAL_OPEN_URL))
-                        .build(),
-                mView);
-
-        // Second context menu fails in opening and is not logged.
-        verifyZeroInteractions(mBasicLoggingApi);
-    }
-
-    @Test
-    public void testMaybeShowTooltip() {
-        TooltipInfo info = mock(TooltipInfo.class);
-        ArgumentCaptor<TooltipCallbackApi> callbackCaptor =
-                ArgumentCaptor.forClass(TooltipCallbackApi.class);
-
-        mStreamActionApi.maybeShowTooltip(info, mView);
-
-        verify(mTooltipApi).maybeShowHelpUi(eq(info), eq(mView), callbackCaptor.capture());
-
-        callbackCaptor.getValue().onShow();
-        verify(mViewElementActionHandler).onElementView(ElementType.TOOLTIP.getNumber());
-
-        callbackCaptor.getValue().onHide(TooltipDismissType.TIMEOUT);
-        verify(mViewElementActionHandler).onElementHide(ElementType.TOOLTIP.getNumber());
-    }
-
-    private void testCommittedDismissWithSnackbar(Type actionType) {
-        ArgumentCaptor<PendingDismissCallback> pendingDismissCallback =
-                ArgumentCaptor.forClass(PendingDismissCallback.class);
-        List<StreamDataOperation> streamDataOperations =
-                Collections.singletonList(StreamDataOperation.getDefaultInstance());
-        UndoAction undoAction =
-                UndoAction.newBuilder().setConfirmationLabel("confirmation").build();
-        switch (actionType) {
-            case DISMISS:
-            case DISMISS_LOCAL:
-                mStreamActionApi.dismiss(
-                        CONTENT_ID, streamDataOperations, undoAction, ACTION_PAYLOAD);
-                break;
-            case NOT_INTERESTED_IN:
-                mStreamActionApi.handleNotInterestedIn(
-                        streamDataOperations, undoAction, ACTION_PAYLOAD, INTEREST_TYPE);
-                break;
-            default:
-                break;
-        }
-
-        verify(mClusterPendingDismissHelper)
-                .triggerPendingDismissForCluster(eq(undoAction), pendingDismissCallback.capture());
-
-        pendingDismissCallback.getValue().onDismissCommitted();
-        switch (actionType) {
-            case DISMISS:
-                verify(mActionManager)
-                        .dismissLocal(
-                                ImmutableList.of(CONTENT_ID), streamDataOperations, SESSION_ID);
-                verify(mBasicLoggingApi)
-                        .onContentDismissed(mContentLoggingData, /*wasCommitted =*/true);
-                verify(mActionManager)
-                        .createAndUploadAction(
-                                CONTENT_ID, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-                break;
-            case DISMISS_LOCAL:
-                verify(mActionManager)
-                        .dismissLocal(
-                                ImmutableList.of(CONTENT_ID), streamDataOperations, SESSION_ID);
-                verify(mBasicLoggingApi)
-                        .onContentDismissed(mContentLoggingData, /*wasCommitted =*/true);
-                break;
-            case NOT_INTERESTED_IN:
-                verify(mActionManager).dismiss(streamDataOperations, SESSION_ID);
-                verify(mBasicLoggingApi)
-                        .onNotInterestedIn(
-                                INTEREST_TYPE, mContentLoggingData, /*wasCommitted =*/true);
-                verify(mActionManager)
-                        .createAndUploadAction(
-                                CONTENT_ID, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-                break;
-            default:
-                break;
-        }
-    }
-
-    private void testRevertedDismissWithSnackbar(Type actionType) {
-        ArgumentCaptor<PendingDismissCallback> pendingDismissCallback =
-                ArgumentCaptor.forClass(PendingDismissCallback.class);
-        String contentId = "contentId";
-        List<StreamDataOperation> streamDataOperations =
-                Collections.singletonList(StreamDataOperation.getDefaultInstance());
-        UndoAction undoAction =
-                UndoAction.newBuilder().setConfirmationLabel("confirmation").build();
-        switch (actionType) {
-            case DISMISS:
-            case DISMISS_LOCAL:
-                mStreamActionApi.dismiss(
-                        contentId, streamDataOperations, undoAction, ACTION_PAYLOAD);
-                break;
-            case NOT_INTERESTED_IN:
-                mStreamActionApi.handleNotInterestedIn(
-                        streamDataOperations, undoAction, ACTION_PAYLOAD, INTEREST_TYPE);
-                break;
-            default:
-                break;
-        }
-
-        verify(mClusterPendingDismissHelper)
-                .triggerPendingDismissForCluster(eq(undoAction), pendingDismissCallback.capture());
-
-        pendingDismissCallback.getValue().onDismissReverted();
-
-        verify(mActionManager, never())
-                .dismissLocal(ImmutableList.of(contentId), streamDataOperations, SESSION_ID);
-        switch (actionType) {
-            case DISMISS:
-            case DISMISS_LOCAL:
-                verify(mBasicLoggingApi)
-                        .onContentDismissed(mContentLoggingData, /*wasCommitted =*/false);
-                break;
-            case NOT_INTERESTED_IN:
-                verify(mBasicLoggingApi)
-                        .onNotInterestedIn(
-                                INTEREST_TYPE, mContentLoggingData, /*wasCommitted =*/false);
-                break;
-            default:
-                break;
-        }
-    }
-
-    private void testDismissNoSnackbar(Type actionType) {
-        String contentId = "contentId";
-        List<StreamDataOperation> streamDataOperations =
-                Collections.singletonList(StreamDataOperation.getDefaultInstance());
-        UndoAction undoAction = UndoAction.getDefaultInstance();
-        switch (actionType) {
-            case DISMISS:
-            case DISMISS_LOCAL:
-                mStreamActionApi.dismiss(
-                        contentId, streamDataOperations, undoAction, ACTION_PAYLOAD);
-                break;
-            case NOT_INTERESTED_IN:
-                mStreamActionApi.handleNotInterestedIn(
-                        streamDataOperations, undoAction, ACTION_PAYLOAD, INTEREST_TYPE);
-                break;
-            default:
-                break;
-        }
-
-        verify(mClusterPendingDismissHelper, never()).triggerPendingDismissForCluster(any(), any());
-
-        switch (actionType) {
-            case DISMISS:
-            case DISMISS_LOCAL:
-                verify(mActionManager)
-                        .dismissLocal(
-                                ImmutableList.of(contentId), streamDataOperations, SESSION_ID);
-                verify(mBasicLoggingApi)
-                        .onContentDismissed(mContentLoggingData, /*wasCommitted =*/true);
-                break;
-            case NOT_INTERESTED_IN:
-                verify(mActionManager).dismiss(streamDataOperations, SESSION_ID);
-                verify(mBasicLoggingApi)
-                        .onNotInterestedIn(
-                                INTEREST_TYPE, mContentLoggingData, /*wasCommitted =*/true);
-                break;
-            default:
-                break;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/CardDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/CardDriverTest.java
deleted file mode 100644
index 3b6a0e2e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/CardDriverTest.java
+++ /dev/null
@@ -1,215 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParserFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild.Type;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.pendingdismiss.ClusterPendingDismissHelper;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater.ViewLoggingUpdater;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager;
-import org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor.StreamOfflineMonitor;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionPayloadProto.FeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.DismissData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Card;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamSwipeExtensionProto.SwipeActionExtension;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link CardDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CardDriverTest {
-    private static final StreamFeature STREAM_FEATURE =
-            StreamFeature.newBuilder().setContent(Content.getDefaultInstance()).build();
-
-    private static final FeedActionPayload SWIPE_ACTION =
-            FeedActionPayload.newBuilder()
-                    .setExtension(FeedAction.feedActionExtension,
-                            FeedAction.newBuilder()
-                                    .setMetadata(FeedActionMetadata.newBuilder().setDismissData(
-                                            DismissData.getDefaultInstance()))
-                                    .build())
-                    .build();
-
-    private static final Card CARD_WITH_SWIPE_ACTION =
-            Card.newBuilder()
-                    .setExtension(SwipeActionExtension.swipeActionExtension,
-                            SwipeActionExtension.newBuilder().setSwipeAction(SWIPE_ACTION).build())
-                    .build();
-
-    private static final int POSITION = 0;
-
-    @Mock
-    private ContentDriver mContentDriverChild;
-    @Mock
-    private ModelFeature mCardModelFeature;
-    @Mock
-    private LeafFeatureDriver mLeafFeatureDriver;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private ClusterPendingDismissHelper mClusterPendingDismissHelper;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private TooltipApi mTooltipApi;
-
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final FakeMainThreadRunner mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-    private CardDriverForTest mCardDriver;
-    private FakeModelCursor mCardCursor;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-
-        // Represents payload of the child of the card.
-        ModelFeature childModelFeature = mock(ModelFeature.class);
-        when(childModelFeature.getStreamFeature()).thenReturn(STREAM_FEATURE);
-
-        // ModelChild containing content.
-        ModelChild pietModelChild = mock(ModelChild.class);
-        when(pietModelChild.getModelFeature()).thenReturn(childModelFeature);
-        when(pietModelChild.getType()).thenReturn(Type.FEATURE);
-
-        // Cursor to transverse the content of a card.
-        mCardCursor = new FakeModelCursor(Lists.newArrayList(pietModelChild));
-
-        // A ModelFeature representing a card.
-        when(mCardModelFeature.getCursor()).thenReturn(mCardCursor);
-        when(mCardModelFeature.getStreamFeature())
-                .thenReturn(StreamFeature.newBuilder().setCard(Card.getDefaultInstance()).build());
-        when(mContentDriverChild.getLeafFeatureDriver()).thenReturn(mLeafFeatureDriver);
-
-        mCardDriver = new CardDriverForTest(mock(ActionApi.class), mock(ActionManager.class),
-                mock(ActionParserFactory.class), mBasicLoggingApi, mCardModelFeature,
-                mock(ModelProvider.class), POSITION, mContentDriverChild,
-                mock(StreamOfflineMonitor.class), mock(ContextMenuManager.class),
-                mock(ViewLoggingUpdater.class));
-    }
-
-    @Test
-    public void testGetLeafFeatureDriver() {
-        assertThat((mCardDriver.getLeafFeatureDriver())).isEqualTo(mLeafFeatureDriver);
-    }
-
-    @Test
-    public void testGetLeafFeatureDriver_reusesPreviousLeafFeatureDriver() {
-        mCardDriver.getLeafFeatureDriver();
-
-        verify(mCardModelFeature).getCursor();
-
-        reset(mCardModelFeature);
-
-        mCardDriver.getLeafFeatureDriver();
-
-        verify(mCardModelFeature, never()).getCursor();
-    }
-
-    @Test
-    public void testGetLeafFeatureDriver_notSwipeable() {
-        when(mCardModelFeature.getStreamFeature())
-                .thenReturn(StreamFeature.newBuilder().setCard(Card.getDefaultInstance()).build());
-
-        mCardDriver.getLeafFeatureDriver();
-
-        assertThat(mCardDriver.mSwipeActionArg).isEqualTo(FeedActionPayload.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetLeafFeatureDriver_swipeable() {
-        when(mCardModelFeature.getStreamFeature())
-                .thenReturn(StreamFeature.newBuilder().setCard(CARD_WITH_SWIPE_ACTION).build());
-
-        mCardDriver.getLeafFeatureDriver();
-
-        assertThat(mCardDriver.mSwipeActionArg).isEqualTo(SWIPE_ACTION);
-    }
-
-    @Test
-    public void testOnDestroy() {
-        mCardDriver.getLeafFeatureDriver();
-        mCardDriver.onDestroy();
-
-        verify(mContentDriverChild).onDestroy();
-    }
-
-    @Test
-    public void testUnboundChild() {
-        ModelChild pietModelChild = mock(ModelChild.class);
-        when(pietModelChild.getType()).thenReturn(Type.UNBOUND);
-
-        // Override setup with an unbound child in the cursor
-        mCardCursor.setModelChildren(Lists.newArrayList(pietModelChild));
-
-        assertThat(mCardDriver.getLeafFeatureDriver()).isNull();
-    }
-
-    @Test
-    public void getLeafFeatureDrivers_childNotAFeature_logsInternalError() {
-        mCardCursor = FakeModelCursor.newBuilder().addUnboundChild().build();
-        when(mCardModelFeature.getCursor()).thenReturn(mCardCursor);
-
-        mCardDriver.getLeafFeatureDriver();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.CARD_CHILD_MISSING_FEATURE);
-    }
-
-    public class CardDriverForTest extends CardDriver {
-        private final ContentDriver mChild;
-        private FeedActionPayload mSwipeActionArg;
-
-        CardDriverForTest(ActionApi actionApi, ActionManager actionManager,
-                ActionParserFactory actionParserFactory, BasicLoggingApi basicLoggingApi,
-                ModelFeature cardModel, ModelProvider modelProvider, int position,
-                ContentDriver childContentDriver, StreamOfflineMonitor streamOfflineMonitor,
-                ContextMenuManager contextMenuManager, ViewLoggingUpdater viewLoggingUpdater) {
-            super(actionApi, actionManager, actionParserFactory, basicLoggingApi, cardModel,
-                    modelProvider, position, mClusterPendingDismissHelper, streamOfflineMonitor,
-                    mContentChangedListener, contextMenuManager, mMainThreadRunner, mConfiguration,
-                    viewLoggingUpdater, mTooltipApi);
-            this.mChild = childContentDriver;
-        }
-
-        @Override
-        ContentDriver createContentDriver(
-                ModelFeature contentModel, FeedActionPayload swipeAction) {
-            this.mSwipeActionArg = swipeAction;
-            return mChild;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ClusterDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ClusterDriverTest.java
deleted file mode 100644
index fece673e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ClusterDriverTest.java
+++ /dev/null
@@ -1,176 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParserFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild.Type;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.pendingdismiss.PendingDismissHandler;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater.ViewLoggingUpdater;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager;
-import org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor.StreamOfflineMonitor;
-import org.chromium.chrome.browser.feed.library.sharedstream.pendingdismiss.PendingDismissCallback;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.UndoAction;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Card;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ClusterDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ClusterDriverTest {
-    private static final StreamFeature CARD_STREAM_FEATURE =
-            StreamFeature.newBuilder().setCard(Card.getDefaultInstance()).build();
-
-    @Mock
-    private CardDriver mCardDriver;
-    @Mock
-    private LeafFeatureDriver mLeafFeatureDriver;
-    @Mock
-    private ModelFeature mClusterModelFeature;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private PendingDismissHandler mPendingDismissHandler;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private TooltipApi mTooltipApi;
-
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final FakeMainThreadRunner mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-    private ClusterDriverForTest mClusterDriver;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-
-        // Child produced by Cursor representing a card.
-        ModelChild cardChild = mock(ModelChild.class);
-        when(cardChild.getType()).thenReturn(Type.FEATURE);
-
-        // ModelFeature representing a card.
-        ModelFeature cardModelFeature = mock(ModelFeature.class);
-        when(cardModelFeature.getStreamFeature()).thenReturn(CARD_STREAM_FEATURE);
-        when(cardChild.getModelFeature()).thenReturn(cardModelFeature);
-
-        // Cursor representing the content of the cluster, which is one card.
-        FakeModelCursor clusterCursor = new FakeModelCursor(Lists.newArrayList(cardChild));
-        when(mClusterModelFeature.getCursor()).thenReturn(clusterCursor);
-
-        when(mCardDriver.getLeafFeatureDriver()).thenReturn(mLeafFeatureDriver);
-
-        mClusterDriver = new ClusterDriverForTest(mock(ActionApi.class), mock(ActionManager.class),
-                mock(ActionParserFactory.class), mBasicLoggingApi, mClusterModelFeature,
-                mock(ModelProvider.class),
-                /* position= */ 0, mCardDriver, mock(StreamOfflineMonitor.class),
-                mock(ContextMenuManager.class), mock(ViewLoggingUpdater.class));
-    }
-
-    @Test
-    public void testGetContentModel() {
-        assertThat(mClusterDriver.getLeafFeatureDriver()).isEqualTo(mLeafFeatureDriver);
-    }
-
-    @Test
-    public void testGetContentModel_reusesPreviousContentModel() {
-        mClusterDriver.getLeafFeatureDriver();
-
-        verify(mClusterModelFeature).getCursor();
-
-        mClusterDriver.getLeafFeatureDriver();
-
-        verifyNoMoreInteractions(mClusterModelFeature);
-    }
-
-    @Test
-    public void testOnDestroy() {
-        mClusterDriver.getLeafFeatureDriver();
-        mClusterDriver.onDestroy();
-
-        verify(mCardDriver).onDestroy();
-    }
-
-    @Test
-    public void testTriggerPendingDismiss() {
-        StreamFeature streamFeature = StreamFeature.newBuilder().setContentId("contentId").build();
-        when(mClusterModelFeature.getStreamFeature()).thenReturn(streamFeature);
-        PendingDismissCallback pendingDismissCallback = Mockito.mock(PendingDismissCallback.class);
-        mClusterDriver.triggerPendingDismissForCluster(
-                UndoAction.getDefaultInstance(), pendingDismissCallback);
-        verify(mPendingDismissHandler)
-                .triggerPendingDismiss(mClusterModelFeature.getStreamFeature().getContentId(),
-                        UndoAction.getDefaultInstance(), pendingDismissCallback);
-    }
-
-    @Test
-    public void getLeafFeatureDrivers_childNotAFeature_logsInternalError() {
-        FakeModelCursor clusterCursor = FakeModelCursor.newBuilder().addUnboundChild().build();
-        when(mClusterModelFeature.getCursor()).thenReturn(clusterCursor);
-
-        mClusterDriver.getLeafFeatureDriver();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.CLUSTER_CHILD_MISSING_FEATURE);
-    }
-
-    @Test
-    public void getLeafFeatureDrivers_childNotACard_logsInternalError() {
-        FakeModelCursor clusterCursor = FakeModelCursor.newBuilder().addCluster().build();
-        when(mClusterModelFeature.getCursor()).thenReturn(clusterCursor);
-
-        mClusterDriver.getLeafFeatureDriver();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.CLUSTER_CHILD_NOT_CARD);
-    }
-
-    class ClusterDriverForTest extends ClusterDriver {
-        private final CardDriver mCardDriver;
-
-        ClusterDriverForTest(ActionApi actionApi, ActionManager actionManager,
-                ActionParserFactory actionParserFactory, BasicLoggingApi basicLoggingApi,
-                ModelFeature clusterModel, ModelProvider modelProvider, int position,
-                CardDriver cardDriver, StreamOfflineMonitor streamOfflineMonitor,
-                ContextMenuManager contextMenuManager, ViewLoggingUpdater viewLoggingUpdater) {
-            super(actionApi, actionManager, actionParserFactory, basicLoggingApi, clusterModel,
-                    modelProvider, position, mPendingDismissHandler, streamOfflineMonitor,
-                    mContentChangedListener, contextMenuManager, mMainThreadRunner, mConfiguration,
-                    viewLoggingUpdater, mTooltipApi);
-            this.mCardDriver = cardDriver;
-        }
-
-        @Override
-        CardDriver createCardDriver(ModelFeature content) {
-            return mCardDriver;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContentDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContentDriverTest.java
deleted file mode 100644
index e1646ca3..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContentDriverTest.java
+++ /dev/null
@@ -1,597 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyListOf;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentMetadata;
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.action.StreamActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ContentLoggingData;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParser;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParserFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.actions.StreamActionApiImpl;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.actions.ViewElementActionHandler;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.pendingdismiss.ClusterPendingDismissHelper;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.PietViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater.ViewLoggingUpdater;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.LoggingListener;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.StreamContentLoggingData;
-import org.chromium.chrome.browser.feed.library.testing.sharedstream.offlinemonitor.FakeStreamOfflineMonitor;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionPayloadProto.FeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.ElementType;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.BasicLoggingMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content.Type;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.OfflineMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.PietContent;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.RepresentationData;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.PietSharedStateItemProto.PietSharedStateItem;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests for {@link ContentDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentDriverTest {
-    private static final long VIEW_MIN_TIME_MS = 200;
-    private static final int POSITION = 1;
-    private static final String CONTENT_URL = "google.com";
-    private static final String CONTENT_TITLE = "title";
-
-    private static final BasicLoggingMetadata BASIC_LOGGING_METADATA =
-            BasicLoggingMetadata.newBuilder().setScore(40).build();
-    private static final RepresentationData REPRESENTATION_DATA =
-            RepresentationData.newBuilder().setUri(CONTENT_URL).setPublishedTimeSeconds(10).build();
-    private static final StreamContentLoggingData CONTENT_LOGGING_DATA =
-            new StreamContentLoggingData(POSITION, BASIC_LOGGING_METADATA, REPRESENTATION_DATA,
-                    /*availableOffline= */ false);
-    private static final OfflineMetadata OFFLINE_METADATA =
-            OfflineMetadata.newBuilder().setTitle(CONTENT_TITLE).build();
-
-    private static final ContentId CONTENT_ID_1 = ContentId.newBuilder()
-                                                          .setContentDomain("piet-shared-state")
-                                                          .setId(2)
-                                                          .setTable("piet-shared-state")
-                                                          .build();
-    private static final ContentId CONTENT_ID_2 = ContentId.newBuilder()
-                                                          .setContentDomain("piet-shared-state")
-                                                          .setId(3)
-                                                          .setTable("piet-shared-state")
-                                                          .build();
-    private static final PietContent PIET_CONTENT =
-            PietContent.newBuilder()
-                    .setFrame(Frame.newBuilder().setStylesheetId("id"))
-                    .addPietSharedStates(CONTENT_ID_1)
-                    .addPietSharedStates(CONTENT_ID_2)
-                    .build();
-    private static final Content CONTENT_NO_OFFLINE_METADATA =
-            Content.newBuilder()
-                    .setBasicLoggingMetadata(BASIC_LOGGING_METADATA)
-                    .setRepresentationData(REPRESENTATION_DATA)
-                    .setType(Type.PIET)
-                    .setExtension(PietContent.pietContentExtension, PIET_CONTENT)
-                    .build();
-    private static final Content CONTENT =
-            CONTENT_NO_OFFLINE_METADATA.toBuilder().setOfflineMetadata(OFFLINE_METADATA).build();
-
-    private static final PietSharedState PIET_SHARED_STATE_1 =
-            PietSharedState.newBuilder()
-                    .addTemplates(Template.newBuilder().setTemplateId("1"))
-                    .build();
-    private static final PietSharedState PIET_SHARED_STATE_2 =
-            PietSharedState.newBuilder()
-                    .addTemplates(Template.newBuilder().setTemplateId("2"))
-                    .build();
-    private static final ImmutableList<PietSharedState> PIET_SHARED_STATES =
-            ImmutableList.of(PIET_SHARED_STATE_1, PIET_SHARED_STATE_2);
-
-    private static final StreamSharedState STREAM_SHARED_STATE_1 =
-            StreamSharedState.newBuilder()
-                    .setPietSharedStateItem(PietSharedStateItem.newBuilder()
-                                                    .setContentId(CONTENT_ID_1)
-                                                    .setPietSharedState(PIET_SHARED_STATE_1))
-                    .build();
-    private static final StreamSharedState STREAM_SHARED_STATE_2 =
-            StreamSharedState.newBuilder()
-                    .setPietSharedStateItem(PietSharedStateItem.newBuilder()
-                                                    .setContentId(CONTENT_ID_2)
-                                                    .setPietSharedState(PIET_SHARED_STATE_2))
-                    .build();
-
-    @Mock
-    private ActionApi mActionApi;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private ActionParserFactory mActionParserFactory;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private ModelFeature mModelFeature;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private ClusterPendingDismissHelper mClusterPendingDismissHelper;
-    @Mock
-    private PietViewHolder mPietViewHolder;
-    @Mock
-    private StreamActionApiImpl mStreamActionApi;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private ActionParser mActionParser;
-    @Mock
-    private ContextMenuManager mContextMenuManager;
-    @Mock
-    private TooltipApi mTooltipApi;
-
-    private FakeStreamOfflineMonitor mFakeStreamOfflineMonitor;
-    private ContentDriver mContentDriver;
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeMainThreadRunner mFakeMainThreadRunner =
-            FakeMainThreadRunner.create(mFakeClock);
-    private final Configuration mConfiguration =
-            new Configuration.Builder().put(ConfigKey.VIEW_MIN_TIME_MS, VIEW_MIN_TIME_MS).build();
-    private ViewLoggingUpdater mViewLoggingUpdater;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-
-        when(mModelFeature.getStreamFeature())
-                .thenReturn(StreamFeature.newBuilder().setContent(CONTENT).build());
-        when(mModelProvider.getSharedState(CONTENT_ID_1)).thenReturn(STREAM_SHARED_STATE_1);
-        when(mModelProvider.getSharedState(CONTENT_ID_2)).thenReturn(STREAM_SHARED_STATE_2);
-        when(mActionParserFactory.build(ArgumentMatchers.<Supplier<ContentMetadata>>any()))
-                .thenReturn(mActionParser);
-        mViewLoggingUpdater = new ViewLoggingUpdater();
-        mFakeStreamOfflineMonitor = FakeStreamOfflineMonitor.create();
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION,
-                FeedActionPayload.getDefaultInstance(), mClusterPendingDismissHelper,
-                mFakeStreamOfflineMonitor, mContentChangedListener, mContextMenuManager,
-                mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater, mTooltipApi) {
-            @Override
-            StreamActionApiImpl createStreamActionApi(ActionApi actionApi,
-                    ActionParser actionParser, ActionManager actionManager,
-                    BasicLoggingApi basicLoggingApi,
-                    Supplier<ContentLoggingData> contentLoggingData, String sessionId,
-                    ContextMenuManager contextMenuManager,
-                    ClusterPendingDismissHelper clusterPendingDismissHelper,
-                    ViewElementActionHandler handler, String contentId, TooltipApi tooltipApi) {
-                return mStreamActionApi;
-            }
-        };
-    }
-
-    @Test
-    public void testBind() {
-        mContentDriver.bind(mPietViewHolder);
-
-        verify(mPietViewHolder)
-                .bind(eq(PIET_CONTENT.getFrame()), eq(PIET_SHARED_STATES), eq(mStreamActionApi),
-                        eq(FeedActionPayload.getDefaultInstance()), any(LoggingListener.class),
-                        eq(mActionParser));
-    }
-
-    @Test
-    public void testMaybeRebind() {
-        mContentDriver.bind(mPietViewHolder);
-        mContentDriver.maybeRebind();
-        verify(mPietViewHolder, times(2))
-                .bind(eq(PIET_CONTENT.getFrame()), eq(PIET_SHARED_STATES), eq(mStreamActionApi),
-                        eq(FeedActionPayload.getDefaultInstance()), any(LoggingListener.class),
-                        eq(mActionParser));
-        verify(mPietViewHolder).unbind();
-    }
-
-    @Test
-    public void testMaybeRebind_nullViewHolder() {
-        // bind/unbind to associated the pietViewHolder with the contentDriver
-        mContentDriver.bind(mPietViewHolder);
-        mContentDriver.unbind();
-        reset(mPietViewHolder);
-
-        mContentDriver.maybeRebind();
-        verify(mPietViewHolder, never())
-                .bind(any(Frame.class), ArgumentMatchers.<List<PietSharedState>>any(),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        any(LoggingListener.class), any(ActionParser.class));
-        verify(mPietViewHolder, never()).unbind();
-    }
-
-    @Test
-    public void testOnViewVisible_visibilityListenerLogsContentViewed() {
-        mContentDriver.bind(mPietViewHolder);
-
-        ArgumentCaptor<LoggingListener> visibilityListenerCaptor =
-                ArgumentCaptor.forClass(LoggingListener.class);
-
-        verify(mPietViewHolder)
-                .bind(any(Frame.class), anyListOf(PietSharedState.class),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        visibilityListenerCaptor.capture(), any(ActionParser.class));
-        LoggingListener listener = visibilityListenerCaptor.getValue();
-        listener.onViewVisible();
-
-        verify(mBasicLoggingApi).onContentViewed(CONTENT_LOGGING_DATA);
-    }
-
-    @Test
-    public void testOnViewVisible_resetVisibility_logsTwice() {
-        mContentDriver.bind(mPietViewHolder);
-
-        ArgumentCaptor<LoggingListener> visibilityListenerCaptor =
-                ArgumentCaptor.forClass(LoggingListener.class);
-
-        verify(mPietViewHolder)
-                .bind(any(Frame.class), anyListOf(PietSharedState.class),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        visibilityListenerCaptor.capture(), any(ActionParser.class));
-        LoggingListener listener = visibilityListenerCaptor.getValue();
-        listener.onViewVisible();
-        mViewLoggingUpdater.resetViewTracking();
-        listener.onViewVisible();
-
-        verify(mBasicLoggingApi, times(2)).onContentViewed(CONTENT_LOGGING_DATA);
-    }
-
-    @Test
-    public void
-    testOnContentClicked_offlineStatusChanges_logsContentClicked_withNewOfflineStatus() {
-        mContentDriver.bind(mPietViewHolder);
-
-        ArgumentCaptor<LoggingListener> visibilityListenerCaptor =
-                ArgumentCaptor.forClass(LoggingListener.class);
-
-        verify(mPietViewHolder)
-                .bind(any(Frame.class), anyListOf(PietSharedState.class),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        visibilityListenerCaptor.capture(), any(ActionParser.class));
-        LoggingListener listener = visibilityListenerCaptor.getValue();
-
-        mFakeStreamOfflineMonitor.setOfflineStatus(CONTENT_URL, true);
-
-        listener.onContentClicked();
-
-        verify(mBasicLoggingApi)
-                .onContentClicked(CONTENT_LOGGING_DATA.createWithOfflineStatus(true));
-    }
-
-    @Test
-    public void testOnContentClicked_visibilityListenerLogsContentClicked() {
-        mContentDriver.bind(mPietViewHolder);
-
-        ArgumentCaptor<LoggingListener> visibilityListenerCaptor =
-                ArgumentCaptor.forClass(LoggingListener.class);
-
-        verify(mPietViewHolder)
-                .bind(any(Frame.class), anyListOf(PietSharedState.class),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        visibilityListenerCaptor.capture(), any(ActionParser.class));
-        LoggingListener listener = visibilityListenerCaptor.getValue();
-        listener.onContentClicked();
-
-        verify(mBasicLoggingApi).onContentClicked(CONTENT_LOGGING_DATA);
-    }
-
-    @Test
-    public void testOnContentSwipe_visibilityListenerLogsContentSwiped() {
-        mContentDriver.bind(mPietViewHolder);
-
-        ArgumentCaptor<LoggingListener> visibilityListenerCaptor =
-                ArgumentCaptor.forClass(LoggingListener.class);
-
-        verify(mPietViewHolder)
-                .bind(any(Frame.class), anyListOf(PietSharedState.class),
-                        any(StreamActionApi.class), any(FeedActionPayload.class),
-                        visibilityListenerCaptor.capture(), any(ActionParser.class));
-        LoggingListener listener = visibilityListenerCaptor.getValue();
-        listener.onContentSwiped();
-
-        verify(mBasicLoggingApi).onContentSwiped(CONTENT_LOGGING_DATA);
-    }
-
-    @Test
-    public void testBind_nullSharedStates() {
-        reset(mModelProvider);
-        when(mModelProvider.getSharedState(CONTENT_ID_1)).thenReturn(null);
-
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION,
-                FeedActionPayload.getDefaultInstance(), mClusterPendingDismissHelper,
-                mFakeStreamOfflineMonitor, mContentChangedListener, mContextMenuManager,
-                mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater, mTooltipApi);
-
-        mContentDriver.bind(mPietViewHolder);
-
-        verify(mPietViewHolder)
-                .bind(eq(PIET_CONTENT.getFrame()),
-                        /* pietSharedStates= */ eq(new ArrayList<>()), any(StreamActionApi.class),
-                        eq(FeedActionPayload.getDefaultInstance()), any(LoggingListener.class),
-                        any(ActionParser.class));
-        verify(mModelProvider).getSharedState(CONTENT_ID_1);
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.NULL_SHARED_STATES);
-    }
-
-    @Test
-    public void testBind_swipeableWithNonDefaultFeedActionPayload() {
-        FeedActionPayload payload = FeedActionPayload.newBuilder()
-                                            .setExtension(FeedAction.feedActionExtension,
-                                                    FeedAction.getDefaultInstance())
-                                            .build();
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION, payload,
-                mClusterPendingDismissHelper, mFakeStreamOfflineMonitor, mContentChangedListener,
-                mContextMenuManager, mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater,
-                mTooltipApi);
-
-        mContentDriver.bind(mPietViewHolder);
-
-        verify(mPietViewHolder)
-                .bind(eq(PIET_CONTENT.getFrame()), eq(PIET_SHARED_STATES),
-                        any(StreamActionApi.class), eq(payload), any(LoggingListener.class),
-                        any(ActionParser.class));
-    }
-
-    @Test
-    public void testOfflineStatusChange() {
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION,
-                FeedActionPayload.getDefaultInstance(), mClusterPendingDismissHelper,
-                mFakeStreamOfflineMonitor, mContentChangedListener, mContextMenuManager,
-                mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater, mTooltipApi);
-
-        mContentDriver.bind(mPietViewHolder);
-
-        reset(mPietViewHolder);
-
-        mFakeStreamOfflineMonitor.setOfflineStatus(CONTENT_URL, true);
-
-        verify(mContentChangedListener).onContentChanged();
-        InOrder inOrder = Mockito.inOrder(mPietViewHolder);
-        inOrder.verify(mPietViewHolder).unbind();
-        inOrder.verify(mPietViewHolder)
-                .bind(eq(PIET_CONTENT.getFrame()), eq(PIET_SHARED_STATES),
-                        any(StreamActionApi.class), eq(FeedActionPayload.getDefaultInstance()),
-                        any(LoggingListener.class), any(ActionParser.class));
-    }
-
-    @Test
-    public void testOfflineStatusChange_noOpWithoutUpdate() {
-        mFakeStreamOfflineMonitor = FakeStreamOfflineMonitor.create();
-
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION,
-                FeedActionPayload.getDefaultInstance(), mClusterPendingDismissHelper,
-                mFakeStreamOfflineMonitor, mContentChangedListener, mContextMenuManager,
-                mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater, mTooltipApi);
-
-        mFakeStreamOfflineMonitor.setOfflineStatus(CONTENT_URL, true);
-        mContentDriver.bind(mPietViewHolder);
-
-        reset(mPietViewHolder);
-
-        mFakeStreamOfflineMonitor.setOfflineStatus(CONTENT_URL, true);
-
-        verifyNoMoreInteractions(mPietViewHolder, mContentChangedListener);
-    }
-
-    @Test
-    public void testNotEnoughInformationForContentMetadata() {
-        assertThat(getContentMetadataFor(CONTENT_NO_OFFLINE_METADATA)).isNull();
-    }
-
-    @Test
-    public void testContentMetadataProvider() {
-        ContentMetadata contentMetadata = getContentMetadataFor(CONTENT);
-
-        assertThat(contentMetadata).isNotNull();
-        assertThat(contentMetadata.getTitle()).isEqualTo(CONTENT_TITLE);
-        assertThat(contentMetadata.getUrl()).isEqualTo(CONTENT_URL);
-    }
-
-    @Test
-    public void testUnbind() {
-        mContentDriver.bind(mPietViewHolder);
-
-        mContentDriver.unbind();
-
-        verify(mPietViewHolder).unbind();
-        assertThat(mContentDriver.isBound()).isFalse();
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewed() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewed_usingOfflineStatusWhenLogging() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-
-        // When the view first happens, the offline status is false.
-        mFakeClock.advance(VIEW_MIN_TIME_MS / 2);
-
-        // When waiting to log the view, the offline status switches to true
-        mFakeStreamOfflineMonitor.setOfflineStatus(CONTENT_URL, true);
-
-        // Advance so that the event is logged
-        mFakeClock.advance(VIEW_MIN_TIME_MS / 2);
-
-        // Should be logged with offline status true.
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(eq(CONTENT_LOGGING_DATA.createWithOfflineStatus(true)),
-                        eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewed_beforeTimeout() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS - 1);
-
-        verify(mBasicLoggingApi, never())
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewedThenHidden() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onElementHide(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-
-        verify(mBasicLoggingApi, never())
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewedThenHiddenThenViewed() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onElementHide(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS - 1);
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-        // Check that it only gets recorded once.
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewedMultiple_differentTypes() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onElementView(ElementType.CARD_LARGE_IMAGE.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(any(ContentLoggingData.class),
-                        eq(ElementType.CARD_LARGE_IMAGE.getNumber()));
-    }
-
-    @Test
-    public void testOnElementView_logsElementViewedMultiple_sameTypes() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-
-        // Make sure it only logs one view.
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnScrollStateChanged() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onScrollStateChanged(RecyclerView.SCROLL_STATE_DRAGGING);
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-        verify(mBasicLoggingApi, never())
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnScrollStateChanged_idle() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE);
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-        verify(mBasicLoggingApi)
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    @Test
-    public void testOnDestroy_cancelsTasks() {
-        mContentDriver.onElementView(ElementType.INTEREST_HEADER.getNumber());
-        mContentDriver.onDestroy();
-        mFakeClock.advance(VIEW_MIN_TIME_MS);
-        verify(mBasicLoggingApi, never())
-                .onVisualElementViewed(
-                        any(ContentLoggingData.class), eq(ElementType.INTEREST_HEADER.getNumber()));
-    }
-
-    /** For Captor of generic. */
-    @SuppressWarnings("unchecked")
-    /*@Nullable*/
-    private ContentMetadata getContentMetadataFor(Content content) {
-        when(mModelFeature.getStreamFeature())
-                .thenReturn(StreamFeature.newBuilder().setContent(content).build());
-
-        reset(mActionParserFactory);
-        when(mActionParserFactory.build(ArgumentMatchers.<Supplier<ContentMetadata>>any()))
-                .thenReturn(mActionParser);
-
-        mContentDriver = new ContentDriver(mActionApi, mActionManager, mActionParserFactory,
-                mBasicLoggingApi, mModelFeature, mModelProvider, POSITION,
-                FeedActionPayload.getDefaultInstance(), mClusterPendingDismissHelper,
-                mFakeStreamOfflineMonitor, mContentChangedListener, mContextMenuManager,
-                mFakeMainThreadRunner, mConfiguration, mViewLoggingUpdater, mTooltipApi);
-
-        ArgumentCaptor<Supplier<ContentMetadata>> contentMetadataSupplierCaptor =
-                ArgumentCaptor.forClass((Class<Supplier<ContentMetadata>>) (Class) Supplier.class);
-
-        verify(mActionParserFactory).build(contentMetadataSupplierCaptor.capture());
-
-        return contentMetadataSupplierCaptor.getValue().get();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContinuationDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContinuationDriverTest.java
deleted file mode 100644
index 1df6789..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ContinuationDriverTest.java
+++ /dev/null
@@ -1,720 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.logging.SpinnerType;
-import org.chromium.chrome.browser.feed.library.api.host.stream.SnackbarApi;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelToken;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompleted;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.ContinuationDriver.CursorChangedListener;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.ContinuationViewHolder;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.LoggingListener;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.SpinnerLogger;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests for {@link ContinuationDriver}.j */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContinuationDriverTest {
-    private static final int POSITION = 1;
-    public static final TokenCompleted EMPTY_TOKEN_COMPLETED =
-            new TokenCompleted(FakeModelCursor.newBuilder().build());
-
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private ModelChild mModelChild;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private ModelToken mModelToken;
-    @Mock
-    private SnackbarApi mSnackbarApi;
-    @Mock
-    private ThreadUtils mThreadUtils;
-    @Mock
-    private CursorChangedListener mCursorChangedListener;
-    @Mock
-    private ContinuationViewHolder mContinuationViewHolder;
-
-    private SpinnerLogger mSpinnerLogger;
-    private Clock mClock;
-    private Context mContext;
-    private ContinuationDriver mContinuationDriver;
-    private Configuration mConfiguration = new Configuration.Builder().build();
-    private View mView;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mClock = new FakeClock();
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mView = new View(mContext);
-        when(mModelProvider.handleToken(any(ModelToken.class))).thenReturn(true);
-        when(mModelChild.getModelToken()).thenReturn(mModelToken);
-        mSpinnerLogger = new SpinnerLogger(mBasicLoggingApi, mClock);
-
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                mConfiguration, mContext, mCursorChangedListener, mModelChild, mModelProvider,
-                POSITION, mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-    }
-
-    @Test
-    public void testHasTokenBeenHandled_returnsTrue_ifSyntheticTokenConsumedImmediately() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder().put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, true).build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        assertThat(mContinuationDriver.hasTokenBeenHandled()).isTrue();
-    }
-
-    @Test
-    public void testHasTokenBeenHandled_returnsFalse_ifSyntheticTokenNotConsumedImmediately() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                mConfiguration, mContext, mCursorChangedListener, mModelChild, mModelProvider,
-                POSITION, mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        assertThat(mContinuationDriver.hasTokenBeenHandled()).isFalse();
-    }
-
-    @Test
-    public void testInitialize_nonSyntheticToken() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        verify(mModelToken).registerObserver(mContinuationDriver);
-        verify(mModelProvider, never()).handleToken(any(ModelToken.class));
-    }
-
-    @Test
-    public void testInitialize_syntheticTokenConsume() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        // Call initialize again to make sure it doesn't duplicate work
-        mContinuationDriver.initialize();
-
-        verify(mModelToken).registerObserver(mContinuationDriver);
-        verify(mModelProvider).handleToken(mModelToken);
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(true));
-    }
-
-    @Test
-    public void testInitialize_syntheticTokenConsume_logsErrorForUnhandledToken() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        when(mModelProvider.handleToken(any(ModelToken.class))).thenReturn(false);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.UNHANDLED_TOKEN);
-    }
-
-    @Test
-    public void testInitialize_syntheticTokenNotConsumed() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, false)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-
-        // Call initialize again to make sure it doesn't duplicate work
-        mContinuationDriver.initialize();
-
-        verify(mModelToken).registerObserver(mContinuationDriver);
-        verify(mModelProvider, never()).handleToken(mModelToken);
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(true));
-    }
-
-    @Test
-    public void testInitialize_doesNotAutoConsumeSyntheticTokenOnRestore() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, false)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, false)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS_WHILE_RESTORING, false)
-                        .build();
-
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock, configuration,
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ true);
-
-        mContinuationDriver.initialize();
-
-        verify(mModelToken).registerObserver(mContinuationDriver);
-        verify(mModelProvider, never()).handleToken(mModelToken);
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(false));
-    }
-
-    @Test
-    public void testInitialize_autoConsumeSyntheticTokenOnRestore() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, false)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, true)
-                        .put(ConfigKey.CONSUME_SYNTHETIC_TOKENS_WHILE_RESTORING, true)
-                        .build();
-
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock, configuration,
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ true);
-
-        mContinuationDriver.initialize();
-
-        // Token should be immediately handled as we create the ContinuationDriver with
-        // forceAutoConsumeSyntheticTokens = true.
-        verify(mModelProvider).handleToken(mModelToken);
-
-        mContinuationDriver.onTokenCompleted(
-                new TokenCompleted(FakeModelCursor.newBuilder().addCard().build()));
-
-        // The listener should be notified that the token was automatically consumed as it was a
-        // synthetic token.
-        verify(mCursorChangedListener)
-                .onNewChildren(any(ModelChild.class), ArgumentMatchers.<List<ModelChild>>any(),
-                        /* wasSynthetic= */ eq(true));
-    }
-
-    @Test
-    public void testBind_immediatePaginationOn() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-
-        mContinuationDriver.initialize();
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class),
-                        /* showSpinner= */ eq(true));
-    }
-
-    @Test
-    public void testBind_immediatePaginationOff() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, false)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-
-        mContinuationDriver.initialize();
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class),
-                        /* showSpinner= */ eq(false));
-    }
-
-    @Test
-    public void testBind_noInitialization() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, false)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-
-        assertThatRunnable(() -> mContinuationDriver.bind(mContinuationViewHolder))
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-
-    @Test
-    public void testShowsSpinnerAfterClick() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        mContinuationDriver.onClick(mView);
-
-        mContinuationDriver.unbind();
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class),
-                        /* showSpinner= */ eq(true));
-    }
-
-    @Test
-    public void testOnDestroy_doesNotUnregisterTokenObserver_ifNotInitialized() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                mConfiguration, mContext, mCursorChangedListener, mModelChild, mModelProvider,
-                POSITION, mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-
-        mContinuationDriver.onDestroy();
-
-        verify(mModelToken, never()).unregisterObserver(any());
-    }
-
-    @Test
-    public void testOnDestroy_unregistersTokenObserver_ifAlreadyInitialized() {
-        mContinuationDriver.onDestroy();
-
-        verify(mModelToken).unregisterObserver(mContinuationDriver);
-    }
-
-    @Test
-    public void testOnClick() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-        mContinuationDriver.onClick(mView);
-
-        verify(mContinuationViewHolder).setShowSpinner(true);
-        verify(mModelProvider).handleToken(mModelToken);
-    }
-
-    @Test
-    public void testBind_listenerLogsMoreButtonClicked() {
-        ArgumentCaptor<LoggingListener> captor = ArgumentCaptor.forClass(LoggingListener.class);
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), captor.capture(), anyBoolean());
-        captor.getValue().onContentClicked();
-
-        verify(mBasicLoggingApi).onMoreButtonClicked(POSITION);
-    }
-
-    @Test
-    public void testBind_listenerLogsMoreButtonViewed() {
-        ArgumentCaptor<LoggingListener> captor = ArgumentCaptor.forClass(LoggingListener.class);
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), captor.capture(), anyBoolean());
-        captor.getValue().onViewVisible();
-
-        verify(mBasicLoggingApi).onMoreButtonViewed(POSITION);
-    }
-
-    @Test
-    public void testBind_listenerDoesNotLogViewTwice() {
-        ArgumentCaptor<LoggingListener> captor = ArgumentCaptor.forClass(LoggingListener.class);
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), captor.capture(), anyBoolean());
-        captor.getValue().onViewVisible();
-        reset(mBasicLoggingApi);
-        captor.getValue().onViewVisible();
-
-        verify(mBasicLoggingApi, never()).onMoreButtonViewed(anyInt());
-    }
-
-    @Test
-    public void testBind_listenerDoesNotLogViewIfSpinnerShown() {
-        ArgumentCaptor<LoggingListener> captor = ArgumentCaptor.forClass(LoggingListener.class);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-        mContinuationDriver.initialize();
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), captor.capture(), anyBoolean());
-        captor.getValue().onViewVisible();
-
-        verify(mBasicLoggingApi, never()).onMoreButtonViewed(anyInt());
-    }
-
-    @Test
-    public void testUnbind() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-        assertThat(mContinuationDriver.isBound()).isTrue();
-        mContinuationDriver.unbind();
-
-        verify(mContinuationViewHolder).unbind();
-        assertThat(mContinuationDriver.isBound()).isFalse();
-    }
-
-    @Test
-    public void testOnTokenCompleted_checksMainThread() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithTokenCompleted(EMPTY_TOKEN_COMPLETED);
-
-        verify(mThreadUtils).checkMainThread();
-    }
-
-    @Test
-    public void testOnTokenCompleted_afterDestroy() {
-        mContinuationDriver.onDestroy();
-
-        mContinuationDriver.onTokenCompleted(EMPTY_TOKEN_COMPLETED);
-
-        verify(mCursorChangedListener, never())
-                .onNewChildren(any(ModelChild.class), ArgumentMatchers.<List<ModelChild>>any(),
-                        /* wasSynthetic= */ eq(false));
-    }
-
-    @Test
-    public void testOnTokenCompleted_extractsModelChildren() {
-        FakeModelCursor cursor =
-                FakeModelCursor.newBuilder().addCard().addCluster().addToken().build();
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-        clickWithTokenCompleted(new TokenCompleted(cursor));
-
-        verify(mCursorChangedListener)
-                .onNewChildren(mModelChild, cursor.getModelChildren(), /* wasSynthetic= */ false);
-        verifyNoMoreInteractions(mSnackbarApi);
-    }
-
-    @Test
-    public void testOnTokenCompleted_createsSnackbar_whenCursorReturnsEmptyPage() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithTokenCompleted(EMPTY_TOKEN_COMPLETED);
-
-        verify(mSnackbarApi)
-                .show(mContext.getString(R.string.ntp_suggestions_fetch_no_new_suggestions));
-        verify(mCursorChangedListener)
-                .onNewChildren(mModelChild, ImmutableList.of(), /* wasSynthetic= */ false);
-    }
-
-    @Test
-    public void testOnTokenCompleted_createsSnackbar_whenCursorJustReturnsToken() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addToken().build();
-        clickWithTokenCompleted(new TokenCompleted(cursor));
-
-        verify(mSnackbarApi)
-                .show(mContext.getString(R.string.ntp_suggestions_fetch_no_new_suggestions));
-        verify(mCursorChangedListener)
-                .onNewChildren(mModelChild, cursor.getModelChildren(), /* wasSynthetic= */ false);
-    }
-
-    @Test
-    public void testOnTokenCompleted_logsSpinnerFinished() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addToken().build();
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-        clickWithTokenCompleted(new TokenCompleted(cursor));
-
-        verify(mBasicLoggingApi)
-                .onSpinnerFinished(/* timeShownMs= */ anyInt(), /* spinnerType= */ anyInt());
-    }
-
-    @Test
-    public void testOnTokenCompleted_doesNotCreateSnackbar_whenTokenFromOthertab() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addToken().build();
-        mContinuationDriver.onTokenCompleted(new TokenCompleted(cursor));
-
-        verifyNoMoreInteractions(mSnackbarApi);
-        verify(mCursorChangedListener)
-                .onNewChildren(mModelChild, cursor.getModelChildren(), /* wasSynthetic= */ false);
-    }
-
-    @Test
-    public void testOnError() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithError();
-
-        verify(mContinuationViewHolder).setShowSpinner(false);
-        verify(mSnackbarApi).show(mContext.getString(R.string.ntp_suggestions_fetch_failed));
-    }
-
-    @Test
-    public void testOnError_hidesSpinnerAfterBeingBound() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ false);
-
-        mContinuationDriver.initialize();
-        mContinuationDriver.bind(mContinuationViewHolder);
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(true));
-
-        mContinuationDriver.unbind();
-        mContinuationDriver.onError(new ModelError(ErrorType.PAGINATION_ERROR, null));
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        verify(mContinuationViewHolder)
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(false));
-        verify(mContinuationViewHolder, never()).setShowSpinner(anyBoolean());
-    }
-
-    @Test
-    public void testBind_logsSpinnerStarted_afterInitializeWithSyntheticToken() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder().put(ConfigKey.CONSUME_SYNTHETIC_TOKENS, true).build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ true);
-        mContinuationDriver.initialize();
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        assertThat(mSpinnerLogger.getSpinnerType()).isEqualTo(SpinnerType.SYNTHETIC_TOKEN);
-    }
-
-    @Test
-    public void testBind_logsSpinnerStarted_ifTriggerImmediatePaginationEnabled() {
-        mContinuationDriver = new ContinuationDriverForTest(mBasicLoggingApi, mClock,
-                new Configuration.Builder()
-                        .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)
-                        .build(),
-                mContext, mCursorChangedListener, mModelChild, mModelProvider, POSITION,
-                mSnackbarApi, mThreadUtils,
-                /* restoring= */ true);
-        mContinuationDriver.initialize();
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        assertThat(mSpinnerLogger.getSpinnerType()).isEqualTo(SpinnerType.INFINITE_FEED);
-    }
-
-    @Test
-    public void testOnClick_logsSpinnerStarted() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        mContinuationDriver.onClick(mView);
-
-        assertThat(mSpinnerLogger.getSpinnerType()).isEqualTo(SpinnerType.MORE_BUTTON);
-    }
-
-    @Test
-    public void testOnClick_logsErrorForUnhandledToken() {
-        when(mModelProvider.handleToken(any(ModelToken.class))).thenReturn(false);
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        mContinuationDriver.onClick(mView);
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.UNHANDLED_TOKEN);
-        verify(mContinuationViewHolder).setShowSpinner(false);
-        verify(mSnackbarApi).show(mContext.getString(R.string.ntp_suggestions_fetch_failed));
-    }
-
-    @Test
-    public void testOnError_logsSpinnerFinished() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithError();
-
-        verify(mBasicLoggingApi)
-                .onSpinnerFinished(/* timeShownMs= */ anyInt(), /* spinnerType= */ anyInt());
-    }
-
-    @Test
-    public void testOnError_logsTokenError() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithError();
-
-        verify(mBasicLoggingApi)
-                .onTokenFailedToComplete(/* wasSynthetic= */ false, /*failureCount=*/1);
-    }
-
-    @Test
-    public void testOnError_logsTokenError_incrementsFailureCount() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithError();
-        clickWithError();
-        clickWithError();
-
-        InOrder inOrder = Mockito.inOrder(mBasicLoggingApi);
-        inOrder.verify(mBasicLoggingApi).onTokenFailedToComplete(/* wasSynthetic= */ false, 1);
-        inOrder.verify(mBasicLoggingApi).onTokenFailedToComplete(/* wasSynthetic= */ false, 2);
-        inOrder.verify(mBasicLoggingApi).onTokenFailedToComplete(/* wasSynthetic= */ false, 3);
-    }
-
-    @Test
-    public void testOnError_syntheticToken_logsTokenError() {
-        when(mModelToken.isSynthetic()).thenReturn(true);
-
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        clickWithError();
-
-        verify(mBasicLoggingApi)
-                .onTokenFailedToComplete(/* wasSynthetic= */ true, /* failureCount= */ 1);
-    }
-
-    @Test
-    public void testOnDestroy_logsSpinnerFinished_ifSpinnerActive() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        mContinuationDriver.onClick(mView);
-        mContinuationDriver.onDestroy();
-
-        verify(mBasicLoggingApi)
-                .onSpinnerDestroyedWithoutCompleting(
-                        /* timeShownMs= */ anyInt(), /* spinnerType= */ anyInt());
-    }
-
-    @Test
-    public void testOnDestroy_doesNotLogSpinnerFinished_ifSpinnerNotActive() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(false);
-        mContinuationDriver.bind(mContinuationViewHolder);
-
-        mContinuationDriver.onDestroy();
-
-        verify(mBasicLoggingApi, never())
-                .onSpinnerDestroyedWithoutCompleting(
-                        /* timeShownMs= */ anyInt(), /* spinnerType= */ anyInt());
-    }
-
-    @Test
-    public void testMaybeRebind() {
-        mContinuationDriver.bind(mContinuationViewHolder);
-        mContinuationDriver.maybeRebind();
-        verify(mContinuationViewHolder, times(2))
-                .bind(eq(mContinuationDriver), any(LoggingListener.class), eq(false));
-        verify(mContinuationViewHolder).unbind();
-    }
-
-    @Test
-    public void testMaybeRebind_nullViewHolder() {
-        // bind/unbind continuationViewHolder so we can then test the driver (and assume the view
-        // holder is null)
-        mContinuationDriver.bind(mContinuationViewHolder);
-        mContinuationDriver.unbind();
-        reset(mContinuationViewHolder);
-
-        mContinuationDriver.maybeRebind();
-        verify(mContinuationViewHolder, never()).unbind();
-        verify(mContinuationViewHolder, never())
-                .bind(any(OnClickListener.class), any(LoggingListener.class), anyBoolean());
-    }
-
-    private void clickWithError() {
-        mContinuationDriver.onClick(mView);
-        mContinuationDriver.onError(new ModelError(ErrorType.PAGINATION_ERROR, null));
-    }
-
-    private void clickWithTokenCompleted(TokenCompleted tokenCompleted) {
-        mContinuationDriver.onClick(mView);
-        mContinuationDriver.onTokenCompleted(tokenCompleted);
-    }
-
-    private final class ContinuationDriverForTest extends ContinuationDriver {
-        ContinuationDriverForTest(BasicLoggingApi basicLoggingApi, Clock clock,
-                Configuration configuration, Context context,
-                CursorChangedListener cursorChangedListener, ModelChild modelChild,
-                ModelProvider modelProvider, int position, SnackbarApi snackbarApi,
-                ThreadUtils threadUtils, boolean restoring) {
-            super(basicLoggingApi, clock, configuration, context, cursorChangedListener, modelChild,
-                    modelProvider, position, snackbarApi, threadUtils, restoring);
-        }
-
-        @Override
-        SpinnerLogger createSpinnerLogger(BasicLoggingApi basicLoggingApi, Clock clock) {
-            return mSpinnerLogger;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/HeaderDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/HeaderDriverTest.java
deleted file mode 100644
index 2a0614e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/HeaderDriverTest.java
+++ /dev/null
@@ -1,117 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.HeaderViewHolder;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.SwipeNotifier;
-import org.chromium.chrome.browser.feed.shared.stream.Header;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link HeaderDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HeaderDriverTest {
-    @Mock
-    private Header mHeader;
-    @Mock
-    private HeaderViewHolder mHeaderViewHolder;
-    @Mock
-    private SwipeNotifier mSwipeNotifier;
-
-    private HeaderDriver mHeaderDriver;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mHeaderDriver = new HeaderDriver(mHeader, mSwipeNotifier);
-    }
-
-    @Test
-    public void testBind() {
-        assertThat(mHeaderDriver.isBound()).isFalse();
-
-        mHeaderDriver.bind(mHeaderViewHolder);
-
-        assertThat(mHeaderDriver.isBound()).isTrue();
-        verify(mHeaderViewHolder).bind(mHeader, mSwipeNotifier);
-    }
-
-    @Test
-    public void testUnbind() {
-        mHeaderDriver.bind(mHeaderViewHolder);
-        assertThat(mHeaderDriver.isBound()).isTrue();
-
-        mHeaderDriver.unbind();
-
-        assertThat(mHeaderDriver.isBound()).isFalse();
-        verify(mHeaderViewHolder).unbind();
-    }
-
-    @Test
-    public void testUnbind_doesNotCallUnbindIfNotBound() {
-        assertThat(mHeaderDriver.isBound()).isFalse();
-
-        mHeaderDriver.unbind();
-        verifyNoMoreInteractions(mHeaderViewHolder);
-    }
-
-    @Test
-    public void testMaybeRebind() {
-        mHeaderDriver.bind(mHeaderViewHolder);
-        mHeaderDriver.maybeRebind();
-        verify(mHeaderViewHolder, times(2)).bind(mHeader, mSwipeNotifier);
-        verify(mHeaderViewHolder).unbind();
-    }
-
-    @Test
-    public void testMaybeRebind_nullViewHolder() {
-        mHeaderDriver.bind(mHeaderViewHolder);
-        mHeaderDriver.unbind();
-        reset(mHeaderViewHolder);
-
-        mHeaderDriver.maybeRebind();
-        verify(mHeaderViewHolder, never()).bind(mHeader, mSwipeNotifier);
-        verify(mHeaderViewHolder, never()).unbind();
-    }
-
-    @Test
-    public void testBind_rebindToSameViewHolder_bindsOnlyOnce() {
-        // Bind twice to the same viewholder.
-        mHeaderDriver.bind(mHeaderViewHolder);
-        mHeaderDriver.bind(mHeaderViewHolder);
-
-        // Only binds to the viewholder once, ignoring the second bind.
-        verify(mHeaderViewHolder).bind(mHeader, mSwipeNotifier);
-    }
-
-    @Test
-    public void testBind_bindWhileBoundToOtherViewHolder_unbindsOldViewHolderBindsNew() {
-        HeaderViewHolder initialViewHolder = mock(HeaderViewHolder.class);
-
-        // Bind to one ViewHolder then another.
-        mHeaderDriver.bind(initialViewHolder);
-
-        mHeaderDriver.bind(mHeaderViewHolder);
-
-        verify(initialViewHolder).unbind();
-        verify(mHeaderViewHolder).bind(mHeader, mSwipeNotifier);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/NoContentDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/NoContentDriverTest.java
deleted file mode 100644
index 06b39a23..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/NoContentDriverTest.java
+++ /dev/null
@@ -1,92 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.NoContentViewHolder;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link NoContentDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class NoContentDriverTest {
-    @Mock
-    private NoContentViewHolder mNoContentViewHolder;
-
-    private NoContentDriver mNoContentDriver;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mNoContentDriver = new NoContentDriver();
-    }
-
-    @Test
-    public void testBind() {
-        assertThat(mNoContentDriver.isBound()).isFalse();
-
-        mNoContentDriver.bind(mNoContentViewHolder);
-
-        assertThat(mNoContentDriver.isBound()).isTrue();
-        verify(mNoContentViewHolder).bind();
-    }
-
-    @Test
-    public void testUnbind() {
-        mNoContentDriver.bind(mNoContentViewHolder);
-        assertThat(mNoContentDriver.isBound()).isTrue();
-
-        mNoContentDriver.unbind();
-
-        assertThat(mNoContentDriver.isBound()).isFalse();
-        verify(mNoContentViewHolder).unbind();
-    }
-
-    @Test
-    public void testUnbind_doesNotCallUnbindIfNotBound() {
-        assertThat(mNoContentDriver.isBound()).isFalse();
-
-        mNoContentDriver.unbind();
-        verifyNoMoreInteractions(mNoContentViewHolder);
-    }
-
-    @Test
-    public void testMaybeRebind() {
-        mNoContentDriver.bind(mNoContentViewHolder);
-        assertThat(mNoContentDriver.isBound()).isTrue();
-
-        mNoContentDriver.maybeRebind();
-        assertThat(mNoContentDriver.isBound()).isTrue();
-        verify(mNoContentViewHolder, times(2)).bind();
-        verify(mNoContentViewHolder).unbind();
-    }
-
-    @Test
-    public void testMaybeRebind_nullViewHolder() {
-        // bind/unbind to associate the noContentViewHolder with the driver
-        mNoContentDriver.bind(mNoContentViewHolder);
-        mNoContentDriver.unbind();
-        reset(mNoContentViewHolder);
-
-        mNoContentDriver.maybeRebind();
-        assertThat(mNoContentDriver.isBound()).isFalse();
-        verify(mNoContentViewHolder, never()).bind();
-        verify(mNoContentViewHolder, never()).unbind();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/StreamDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/StreamDriverTest.java
deleted file mode 100644
index 1a43f78..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/StreamDriverTest.java
+++ /dev/null
@@ -1,1084 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor.getCardModelFeatureWithCursor;
-
-import android.app.Activity;
-import android.content.Context;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.feed.library.api.host.action.ActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ZeroStateShowReason;
-import org.chromium.chrome.browser.feed.library.api.host.stream.SnackbarApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.SnackbarCallbackApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParserFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.knowncontent.FeedKnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild.Type;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.RemoveTrackingFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.StreamDriver.StreamContentListener;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.testing.FakeFeatureDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.drivers.testing.FakeLeafFeatureDriver;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.BasicStreamScrollMonitor;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.ScrollRestorer;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater.ViewLoggingUpdater;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager;
-import org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor.StreamOfflineMonitor;
-import org.chromium.chrome.browser.feed.library.sharedstream.pendingdismiss.PendingDismissCallback;
-import org.chromium.chrome.browser.feed.library.sharedstream.removetrackingfactory.StreamRemoveTrackingFactory;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelChild;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelFeature;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FeatureChangeBuilder;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason.Reason;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.UndoAction;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Card;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Cluster;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests for {@link StreamDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamDriverTest {
-    private static final String CONTENT_ID = "contentID";
-    private FakeModelCursor mCursorWithInvalidChildren;
-
-    private static final FakeModelCursor EMPTY_MODEL_CURSOR = FakeModelCursor.newBuilder().build();
-
-    private static final ModelFeature UNBOUND_CARD =
-            FakeModelFeature.newBuilder()
-                    .setStreamFeature(
-                            StreamFeature.newBuilder().setCard(Card.getDefaultInstance()).build())
-                    .build();
-
-    @Mock
-    private ActionApi mActionApi;
-    @Mock
-    private ActionManager mActionManager;
-    @Mock
-    private ActionParserFactory mActionParserFactory;
-    @Mock
-    private ModelFeature mStreamFeature;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private ContinuationDriver mContinuationDriver;
-    @Mock
-    private NoContentDriver mNoContentDriver;
-    @Mock
-    private ZeroStateDriver mZeroStateDriver;
-    @Mock
-    private StreamContentListener mContentlistener;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private SnackbarApi mSnackbarApi;
-    @Mock
-    private ScrollRestorer mScrollRestorer;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private FeedKnownContent mFeedKnownContent;
-    @Mock
-    private StreamOfflineMonitor mStreamOfflineMonitor;
-    @Mock
-    private PendingDismissCallback mPendingDismissCallback;
-    @Mock
-    private TooltipApi mTooltipApi;
-    @Mock
-    private BasicStreamScrollMonitor mScrollmonitor;
-
-    @Captor
-    private ArgumentCaptor<List<LeafFeatureDriver>> mFeatureDriversCaptor;
-
-    private StreamDriverForTest mStreamDriver;
-    private Configuration mConfiguration = new Configuration.Builder().build();
-    private Context mContext;
-    private Clock mClock;
-    private ThreadUtils mThreadUtils;
-    private final FakeMainThreadRunner mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-    private final ViewLoggingUpdater mViewLoggingUpdater = new ViewLoggingUpdater();
-
-    private static FakeModelChild createFakeClusterModelChild() {
-        return FakeModelChild.newBuilder()
-                .setModelFeature(
-                        FakeModelFeature.newBuilder()
-                                .setStreamFeature(StreamFeature.newBuilder()
-                                                          .setCluster(Cluster.getDefaultInstance())
-                                                          .build())
-                                .build())
-                .build();
-    }
-
-    @Before
-    public void setup() {
-        FakeModelFeature feature =
-                FakeModelFeature.newBuilder()
-                        .setStreamFeature(StreamFeature.newBuilder()
-                                                  .setContent(Content.getDefaultInstance())
-                                                  .build())
-                        .build();
-        mCursorWithInvalidChildren =
-                FakeModelCursor.newBuilder()
-                        .addChild(FakeModelChild.newBuilder().setModelFeature(feature).build())
-                        .build();
-
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mClock = new FakeClock();
-
-        when(mContinuationDriver.getLeafFeatureDriver()).thenReturn(mContinuationDriver);
-        when(mNoContentDriver.getLeafFeatureDriver()).thenReturn(mNoContentDriver);
-        when(mZeroStateDriver.getLeafFeatureDriver()).thenReturn(mZeroStateDriver);
-        when(mZeroStateDriver.isSpinnerShowing()).thenReturn(false);
-
-        when(mModelProvider.getRootFeature()).thenReturn(mStreamFeature);
-        when(mModelProvider.getCurrentState()).thenReturn(State.READY);
-        mThreadUtils = new ThreadUtils();
-
-        mStreamDriver = createNonRestoringStreamDriver();
-
-        mStreamDriver.setStreamContentListener(mContentlistener);
-    }
-
-    @Test
-    public void testConstruction() {
-        ArgumentCaptor<RemoveTrackingFactory> removeTrackingFactoryArgumentCaptor =
-                ArgumentCaptor.forClass(RemoveTrackingFactory.class);
-
-        verify(mModelProvider).enableRemoveTracking(removeTrackingFactoryArgumentCaptor.capture());
-
-        assertThat(removeTrackingFactoryArgumentCaptor.getValue())
-                .isInstanceOf(StreamRemoveTrackingFactory.class);
-    }
-
-    @Test
-    public void testBuildChildren() {
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addCard().addCluster().addToken().build());
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).hasSize(3);
-
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(getLeafFeatureDriverFromCard(0));
-        assertThat(leafFeatureDrivers.get(1)).isEqualTo(getLeafFeatureDriverFromCluster(1));
-        assertThat(leafFeatureDrivers.get(2)).isEqualTo(mContinuationDriver);
-
-        verify(mContinuationDriver).initialize();
-        verify(mStreamOfflineMonitor).requestOfflineStatusForNewContent();
-    }
-
-    @Test
-    public void testBuildChildren_unboundContent() {
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addChild(UNBOUND_CARD).addCard().build());
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(getLeafFeatureDriverFromCard(1));
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.FAILED_TO_CREATE_LEAF);
-    }
-
-    @Test
-    public void testBuildChildren_unboundChild_logsInternalError() {
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addUnboundChild().build());
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.TOP_LEVEL_UNBOUND_CHILD);
-    }
-
-    @Test
-    public void testBuildChildren_initializing() {
-        when(mModelProvider.getRootFeature()).thenReturn(null);
-        when(mModelProvider.getCurrentState()).thenReturn(State.INITIALIZING);
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(ZeroStateDriver.class);
-    }
-
-    @Test
-    public void testBuildChildren_nullRootFeature_logsInternalError() {
-        when(mModelProvider.getRootFeature()).thenReturn(null);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.NO_ROOT_FEATURE);
-    }
-
-    @Test
-    public void testCreateChildren_invalidFeatureType_logsInternalError() {
-        when(mStreamFeature.getCursor()).thenReturn(mCursorWithInvalidChildren);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.TOP_LEVEL_INVALID_FEATURE_TYPE);
-    }
-
-    @Test
-    public void testMaybeRestoreScroll() {
-        mStreamDriver = createRestoringStreamDriver();
-
-        when(mStreamFeature.getCursor()).thenReturn(FakeModelCursor.newBuilder().addCard().build());
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.maybeRestoreScroll();
-
-        verify(mScrollRestorer).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testMaybeRestoreScroll_withToken() {
-        mStreamDriver = createRestoringStreamDriver();
-
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addCard().addCluster().addToken().build());
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.maybeRestoreScroll();
-
-        // Should restore scroll if a non-synthetic token is last
-        verify(mScrollRestorer).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testMaybeRestoreScroll_withSyntheticToken() {
-        mStreamDriver = createRestoringStreamDriver();
-
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder()
-                                    .addCard()
-                                    .addCluster()
-                                    .addSyntheticToken()
-                                    .build());
-        when(mContinuationDriver.hasTokenBeenHandled()).thenReturn(true);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.maybeRestoreScroll();
-
-        // Should never restore scroll if a synthetic token is last
-        verify(mScrollRestorer, never()).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testMaybeRestoreScroll_notRestoring_doesNotScroll() {
-        mStreamDriver = createNonRestoringStreamDriver();
-
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder()
-                                    .addCard()
-                                    .addCluster()
-                                    .addSyntheticToken()
-                                    .build());
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.maybeRestoreScroll();
-
-        // Should never restore scroll if created in a non-restoring state.
-        verify(mScrollRestorer, never()).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testContinuationToken_createsContinuationContentModel() {
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addToken().build());
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(2);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(mNoContentDriver);
-        assertThat(leafFeatureDrivers.get(1)).isEqualTo(mContinuationDriver);
-    }
-
-    @Test
-    public void testContinuationToken_tokenHandling() {
-        mStreamDriver = createStreamDriver(/* restoring= */ true, /* isInitialLoad= */ false);
-
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addToken().build();
-        FakeModelCursor newCursor = FakeModelCursor.newBuilder().addCluster().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(2);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(mNoContentDriver);
-        assertThat(leafFeatureDrivers.get(1)).isEqualTo(mContinuationDriver);
-
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(0), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-        leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(1);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(getLeafFeatureDriverFromCluster(1));
-
-        // If the above two assertions pass, this is also guaranteed to pass. This is just to
-        // explicitly check that the ContinuationDriver has been removed.
-        assertThat(leafFeatureDrivers).doesNotContain(mContinuationDriver);
-
-        verify(mScrollRestorer).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testContinuationToken_tokenHandling_newSyntheticToken() {
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addSyntheticToken().build();
-        FakeModelCursor newCursor =
-                FakeModelCursor.newBuilder().addCluster().addSyntheticToken().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-        when(mContinuationDriver.hasTokenBeenHandled()).thenReturn(true);
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(2);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(mNoContentDriver);
-        assertThat(leafFeatureDrivers.get(1)).isEqualTo(mContinuationDriver);
-
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(0), newCursor.getModelChildren(),
-                /* wasSynthetic = */ true);
-        leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(2);
-        assertThat(leafFeatureDrivers.get(0)).isEqualTo(getLeafFeatureDriverFromCluster(1));
-        assertThat(leafFeatureDrivers.get(1)).isEqualTo(mContinuationDriver);
-
-        verify(mScrollRestorer, never()).maybeRestoreScroll();
-    }
-
-    @Test
-    public void testContinuationToken_tokenHandling_notifiesObservers() {
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        FakeModelCursor newCursor = FakeModelCursor.newBuilder().addCluster().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(1), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-
-        verify(mContentlistener).notifyContentRemoved(1);
-        verify(mContentlistener)
-                .notifyContentsAdded(1, Lists.newArrayList(getLeafFeatureDriverFromCluster(2)));
-        verify(mStreamOfflineMonitor, times(2)).requestOfflineStatusForNewContent();
-    }
-
-    @Test
-    public void testContinuationToken_tokenChildrenAddedAtTokenPosition() {
-        FakeModelCursor initialCursor =
-                FakeModelCursor.newBuilder().addCluster().addToken().build();
-        FakeModelCursor newCursor = FakeModelCursor.newBuilder().addCluster().addToken().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(1), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(3);
-        assertThat(leafFeatureDrivers)
-                .containsExactly(getLeafFeatureDriverFromCluster(0),
-                        getLeafFeatureDriverFromCluster(2), mContinuationDriver);
-    }
-
-    @Test
-    public void testContinuationToken_tokenChildrenAddedAtTokenPosition_tokenNotAtEnd() {
-        FakeModelCursor initialCursor =
-                FakeModelCursor.newBuilder().addCluster().addToken().addCluster().build();
-        FakeModelCursor newCursor =
-                FakeModelCursor.newBuilder().addCluster().addCard().addCluster().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(1), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(5);
-        assertThat(leafFeatureDrivers)
-                .containsExactly(getLeafFeatureDriverFromCluster(0),
-                        getLeafFeatureDriverFromCluster(2), getLeafFeatureDriverFromCluster(3),
-                        getLeafFeatureDriverFromCard(4), getLeafFeatureDriverFromCluster(5));
-    }
-
-    @Test
-    public void testContinuationToken_tokenNotFound() {
-        FakeModelCursor initialCursor =
-                FakeModelCursor.newBuilder().addCluster().addToken().addCluster().build();
-        FakeModelCursor badCursor = FakeModelCursor.newBuilder().addCard().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-        assertThatRunnable(()
-                                   -> mStreamDriver.onNewChildren(badCursor.getChildAt(0),
-                                           badCursor.getModelChildren(),
-                                           /* wasSynthetic = */ false))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testOnChange_remove() {
-        FakeModelCursor fakeModelCursor =
-                FakeModelCursor.newBuilder().addCard().addCard().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).hasSize(4);
-
-        mStreamDriver.onChange(new FeatureChangeBuilder()
-                                       .addChildForRemoval(fakeModelCursor.getChildAt(1))
-                                       .addChildForRemoval(fakeModelCursor.getChildAt(2))
-                                       .build());
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers())
-                .containsExactly(leafFeatureDrivers.get(0), leafFeatureDrivers.get(3));
-    }
-
-    @Test
-    public void testOnChange_addsZeroState_whenFeatureDriversEmpty() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().build();
-
-        initializeStreamDriverAndDismissAllFeatureChildren(fakeModelCursor);
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(1);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(ZeroStateDriver.class);
-    }
-
-    @Test
-    public void testOnChange_dismissesLastDriver_logsContentDismissed() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().build();
-
-        initializeStreamDriverAndDismissAllFeatureChildren(fakeModelCursor);
-
-        verify(mBasicLoggingApi).onZeroStateShown(ZeroStateShowReason.CONTENT_DISMISSED);
-    }
-
-    @Test
-    public void testGetLeafFeatureDrivers_addsZeroState_withNoModelChildren() {
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(leafFeatureDrivers.get(0)).isInstanceOf(ZeroStateDriver.class);
-    }
-
-    @Test
-    public void testGetLeafFeatureDrivers_noContent_logsNoContent() {
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi).onZeroStateShown(ZeroStateShowReason.NO_CONTENT);
-    }
-
-    @Test
-    public void testGetLeafFeatureDrivers_doesNotAddZeroState_ifInitialLoad() {
-        mStreamDriver = createStreamDriver(/* restoring= */ false, /* isInitialLoad= */ true);
-
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).isEmpty();
-    }
-
-    @Test
-    public void testGetLeafFeatureDrivers_doesNotAddZeroState_ifRestoring() {
-        mStreamDriver = createStreamDriver(/* restoring= */ true, /* isInitialLoad= */ false);
-
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).isEmpty();
-    }
-
-    @Test
-    public void testShowZeroState_createsZeroState() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(2);
-
-        mStreamDriver.showZeroState(ZeroStateShowReason.ERROR);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(leafFeatureDrivers.get(0)).isInstanceOf(ZeroStateDriver.class);
-    }
-
-    @Test
-    public void testShowZeroState_logsZeroStateReason() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-        mStreamDriver.showZeroState(ZeroStateShowReason.ERROR);
-
-        verify(mBasicLoggingApi).onZeroStateShown(ZeroStateShowReason.ERROR);
-    }
-
-    @Test
-    public void testShowZeroState_notifiesContentsCleared() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        mStreamDriver.showSpinner();
-
-        verify(mContentlistener).notifyContentsCleared();
-    }
-
-    @Test
-    public void testShowZeroState_destroysFeatureDrivers() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addToken().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.showSpinner();
-
-        verify(mContinuationDriver).onDestroy();
-    }
-
-    @Test
-    public void testOnChange_addsNoContentCard_withJustContinuationDriver() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-
-        initializeStreamDriverAndDismissAllFeatureChildren(fakeModelCursor);
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(2);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(NoContentDriver.class);
-    }
-
-    @Test
-    public void testContinuationToken_removesNoContentCard() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-
-        initializeStreamDriverAndDismissAllFeatureChildren(fakeModelCursor);
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(2);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(NoContentDriver.class);
-
-        FakeModelCursor newCursor = FakeModelCursor.newBuilder().addCard().build();
-        mStreamDriver.onNewChildren(fakeModelCursor.getChildAt(1), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(1);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(FakeLeafFeatureDriver.class);
-    }
-
-    @Test
-    public void testContinuationToken_replacesNoContentCardWithZeroState() {
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        initializeStreamDriverAndDismissAllFeatureChildren(initialCursor);
-        mStreamDriver.onNewChildren(
-                initialCursor.getChildAt(1), ImmutableList.of(), /* wasSynthetic = */ false);
-
-        verify(mNoContentDriver).onDestroy();
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(1);
-        assertThat(mStreamDriver.getLeafFeatureDrivers().get(0))
-                .isInstanceOf(ZeroStateDriver.class);
-    }
-
-    @Test
-    public void testContinuationToken_logsContinuationTokenPayload() {
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(1),
-                FakeModelCursor.newBuilder()
-                        .addCluster()
-                        .addCard()
-                        .addCard()
-                        .addToken()
-                        .build()
-                        .getModelChildren(),
-                true);
-
-        verify(mBasicLoggingApi)
-                .onTokenCompleted(
-                        /* wasSynthetic= */ true, /* contentCount= */ 3, /* tokenCount= */ 1);
-    }
-
-    @Test
-    public void testOnChange_remove_notifiesListener() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.onChange(new FeatureChangeBuilder()
-                                       .addChildForRemoval(fakeModelCursor.getChildAt(0))
-                                       .build());
-
-        verify(mContentlistener).notifyContentRemoved(0);
-        verifyNoMoreInteractions(mContentlistener);
-    }
-
-    @Test
-    public void testOnChange_addAndRemoveContent() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.onChange(new FeatureChangeBuilder()
-                                       .addChildForRemoval(fakeModelCursor.getChildAt(0))
-                                       .addChildForAppending(createFakeClusterModelChild())
-                                       .build());
-
-        InOrder inOrder = Mockito.inOrder(mContentlistener);
-
-        inOrder.verify(mContentlistener).notifyContentRemoved(0);
-        inOrder.verify(mContentlistener)
-                .notifyContentsAdded(eq(1), mFeatureDriversCaptor.capture());
-        inOrder.verifyNoMoreInteractions();
-
-        assertThat(mFeatureDriversCaptor.getValue()).hasSize(1);
-        assertThat(mFeatureDriversCaptor.getValue().get(0))
-                .isEqualTo(getLeafFeatureDriverFromCluster(2));
-    }
-
-    @Test
-    public void testOnChange_addContent() {
-        FakeModelCursor fakeModelCursor = FakeModelCursor.newBuilder().addCard().addCard().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        mStreamDriver.getLeafFeatureDrivers();
-
-        reset(mContentlistener);
-
-        mStreamDriver.onChange(new FeatureChangeBuilder()
-                                       .addChildForAppending(createFakeClusterModelChild())
-                                       .build());
-
-        InOrder inOrder = Mockito.inOrder(mContentlistener);
-
-        inOrder.verify(mContentlistener)
-                .notifyContentsAdded(eq(2), mFeatureDriversCaptor.capture());
-        inOrder.verifyNoMoreInteractions();
-
-        assertThat(mFeatureDriversCaptor.getValue()).hasSize(1);
-        assertThat(mFeatureDriversCaptor.getValue().get(0))
-                .isEqualTo(getLeafFeatureDriverFromCluster(2));
-        verify(mStreamOfflineMonitor, times(2)).requestOfflineStatusForNewContent();
-    }
-
-    @Test
-    public void testOnDestroy() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        initializeStreamDriverAndDismissAllFeatureChildren(cursor);
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers())
-                .containsExactly(mNoContentDriver, mContinuationDriver);
-        mStreamDriver.onDestroy();
-
-        verify(mNoContentDriver).onDestroy();
-        verify(mContinuationDriver).onDestroy();
-        verify(mStreamFeature).unregisterObserver(mStreamDriver);
-    }
-
-    @Test
-    public void testOnDestroy_clearsFeatureDrivers() {
-        mStreamDriver = createStreamDriver(false, true);
-        when(mStreamFeature.getCursor()).thenReturn(FakeModelCursor.newBuilder().addCard().build());
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).hasSize(1);
-        mStreamDriver.onDestroy();
-
-        assertThat(mStreamDriver.getLeafFeatureDrivers()).isEmpty();
-    }
-
-    @Test
-    public void testHasContent_returnsTrue() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        when(mStreamFeature.getCursor()).thenReturn(cursor);
-        mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(mStreamDriver.hasContent()).isTrue();
-    }
-
-    @Test
-    public void testHasContent_returnsFalse_withNoContentCard() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        initializeStreamDriverAndDismissAllFeatureChildren(cursor);
-
-        assertThat(mStreamDriver.hasContent()).isFalse();
-    }
-
-    @Test
-    public void testHasContent_returnsFalse_withZeroState() {
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(mStreamDriver.hasContent()).isFalse();
-    }
-
-    @Test
-    public void testAutoConsumeSyntheticTokensOnRestore() {
-        FakeModelCursor initialCursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-        FakeModelCursor newCursor = FakeModelCursor.newBuilder().addToken().build();
-
-        when(mStreamFeature.getCursor()).thenReturn(initialCursor);
-
-        mStreamDriver = createStreamDriver(/* restoring= */ true, /* isInitialLoad= */ false);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(mStreamDriver.mWasRestoringDuringLastContinuationDriver).isTrue();
-
-        // Restoring scroll indicates that restore is over, and further continuation drivers will
-        // not be forced to consume synthetic tokens.
-        mStreamDriver.maybeRestoreScroll();
-
-        mStreamDriver.onNewChildren(initialCursor.getChildAt(1), newCursor.getModelChildren(),
-                /* wasSynthetic = */ false);
-
-        assertThat(mStreamDriver.mWasRestoringDuringLastContinuationDriver).isFalse();
-    }
-
-    @Test
-    public void testTriggerPendingDismiss_noAction() {
-        setCards(CONTENT_ID);
-        SnackbarCallbackApi snackbarCallbackApi = triggerPendingDismiss(
-                UndoAction.newBuilder().setConfirmationLabel("confirmation").build(), CONTENT_ID);
-
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isTrue();
-
-        snackbarCallbackApi.onDismissNoAction();
-
-        verify(mPendingDismissCallback).onDismissCommitted();
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isTrue();
-    }
-
-    @Test
-    public void testTriggerPendingDismiss_actionTaken_lastCard() {
-        setCards(CONTENT_ID);
-        SnackbarCallbackApi snackbarCallbackApi = triggerPendingDismiss(
-                UndoAction.newBuilder().setConfirmationLabel("confirmation").build(), CONTENT_ID);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isTrue();
-
-        snackbarCallbackApi.onDismissedWithAction();
-
-        verify(mPendingDismissCallback).onDismissReverted();
-        leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isFalse();
-    }
-
-    @Test
-    public void testTriggerPendingDismiss_actionTaken_multipleCards() {
-        setCards(CONTENT_ID, CONTENT_ID + "1");
-        SnackbarCallbackApi snackbarCallbackApi = triggerPendingDismiss(
-                UndoAction.newBuilder().setConfirmationLabel("confirmation").build(), CONTENT_ID);
-
-        List<LeafFeatureDriver> leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(1);
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isFalse();
-
-        snackbarCallbackApi.onDismissedWithAction();
-
-        verify(mPendingDismissCallback).onDismissReverted();
-        leafFeatureDrivers = mStreamDriver.getLeafFeatureDrivers();
-        assertThat(leafFeatureDrivers).hasSize(2);
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isFalse();
-    }
-
-    @Test
-    public void testTriggerPendingDismiss_actionStringSent() {
-        setCards(CONTENT_ID);
-        triggerPendingDismiss(UndoAction.newBuilder()
-                                      .setConfirmationLabel("conf")
-                                      .setUndoLabel("undo label")
-                                      .build(),
-                CONTENT_ID);
-    }
-
-    @Test
-    public void testIsZeroStateBeingShown_returnsTrue_ifZeroState() {
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-        mStreamDriver.getLeafFeatureDrivers();
-
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isTrue();
-    }
-
-    @Test
-    public void testIsZeroStateBeingShown_returnsFalse_whenNoContentCardShowing() {
-        FakeModelCursor cursor = FakeModelCursor.newBuilder().addCard().addToken().build();
-
-        // Dismisses all features, but not the token, so the no-content state will be showing.
-        initializeStreamDriverAndDismissAllFeatureChildren(cursor);
-
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isFalse();
-    }
-
-    @Test
-    public void testIsZeroStateBeingShown_returnsFalse_ifZeroStateIsSpinner() {
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addCard().addToken().build());
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.showSpinner();
-
-        assertThat(mStreamDriver.isZeroStateBeingShown()).isFalse();
-    }
-
-    @Test
-    public void testSetModelProvider_setsModelProviderOnTheZeroState() {
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.setModelProviderForZeroState(mModelProvider);
-
-        verify(mZeroStateDriver).setModelProvider(mModelProvider);
-    }
-
-    @Test
-    public void testRefreshFromZeroState_resultsInZeroState_logsNoNewContent() {
-        mStreamDriver = createStreamDriverFromZeroStateRefresh();
-
-        when(mStreamFeature.getCursor()).thenReturn(EMPTY_MODEL_CURSOR);
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi)
-                .onZeroStateRefreshCompleted(/* newContentCount= */ 0, /* newTokenCount= */ 0);
-    }
-
-    @Test
-    public void testRefreshFromZeroState_addsContent_logsContent() {
-        mStreamDriver = createStreamDriverFromZeroStateRefresh();
-
-        when(mStreamFeature.getCursor())
-                .thenReturn(FakeModelCursor.newBuilder().addClusters(10).addToken().build());
-        mStreamDriver.getLeafFeatureDrivers();
-
-        verify(mBasicLoggingApi)
-                .onZeroStateRefreshCompleted(/* newContentCount= */ 10, /* newTokenCount= */ 1);
-    }
-
-    private void initializeStreamDriverAndDismissAllFeatureChildren(
-            FakeModelCursor fakeModelCursor) {
-        when(mStreamFeature.getCursor()).thenReturn(fakeModelCursor);
-
-        mStreamDriver.getLeafFeatureDrivers();
-
-        FeatureChangeBuilder dismissAllChildrenBuilder = new FeatureChangeBuilder();
-
-        for (ModelChild child : fakeModelCursor.getModelChildren()) {
-            if (child.getType() == Type.FEATURE) {
-                dismissAllChildrenBuilder.addChildForRemoval(child);
-            }
-        }
-
-        mStreamDriver.onChange(dismissAllChildrenBuilder.build());
-    }
-
-    private StreamDriverForTest createNonRestoringStreamDriver() {
-        return createStreamDriver(/* restoring= */ false, /* isInitialLoad= */ false);
-    }
-
-    private StreamDriverForTest createRestoringStreamDriver() {
-        return createStreamDriver(/* restoring= */ true, /* isInitialLoad= */ true);
-    }
-
-    private StreamDriverForTest createStreamDriverFromZeroStateRefresh() {
-        return createStreamDriver(
-                /* restoring= */ false,
-                /* isInitialLoad= */ false,
-                UiRefreshReason.newBuilder().setReason(Reason.ZERO_STATE).build());
-    }
-
-    private StreamDriverForTest createStreamDriver(
-            boolean restoring, boolean isInitialLoad, UiRefreshReason uiRefreshReason) {
-        return new StreamDriverForTest(
-                mModelProvider, mThreadUtils, restoring, isInitialLoad, uiRefreshReason);
-    }
-
-    private StreamDriverForTest createStreamDriver(boolean restoring, boolean isInitialLoad) {
-        return new StreamDriverForTest(mModelProvider, mThreadUtils, restoring, isInitialLoad,
-                UiRefreshReason.getDefaultInstance());
-    }
-
-    // TODO: Instead of just checking that the ModelFeature is of the correct type, check
-    // that it is the one created by the FakeModelCursor.Builder.
-    private LeafFeatureDriver getLeafFeatureDriverFromCard(int i) {
-        FakeFeatureDriver featureDriver = (FakeFeatureDriver) mStreamDriver.mChildrenCreated.get(i);
-        assertThat(featureDriver.getModelFeature().getStreamFeature().hasCard()).isTrue();
-        return mStreamDriver.mChildrenCreated.get(i).getLeafFeatureDriver();
-    }
-
-    // TODO: Instead of just checking that the ModelFeature is of the correct type, check
-    // that it is the one created by the FakeModelCursor.Builder.
-    private LeafFeatureDriver getLeafFeatureDriverFromCluster(int i) {
-        FakeFeatureDriver featureDriver = (FakeFeatureDriver) mStreamDriver.mChildrenCreated.get(i);
-        assertThat(featureDriver.getModelFeature().getStreamFeature().hasCluster()).isTrue();
-        return mStreamDriver.mChildrenCreated.get(i).getLeafFeatureDriver();
-    }
-
-    private void setCards(String... contentId) {
-        FakeModelCursor.Builder cursor = FakeModelCursor.newBuilder();
-        for (String id : contentId) {
-            cursor.addChild(
-                    FakeModelChild.newBuilder()
-                            .setModelFeature(getCardModelFeatureWithCursor(EMPTY_MODEL_CURSOR))
-                            .setContentId(id)
-                            .build());
-        }
-        when(mStreamFeature.getCursor()).thenReturn(cursor.build());
-    }
-
-    private SnackbarCallbackApi triggerPendingDismiss(UndoAction undoAction, String contentId) {
-        ArgumentCaptor<SnackbarCallbackApi> snackbarCallbackApi =
-                ArgumentCaptor.forClass(SnackbarCallbackApi.class);
-
-        // Causes StreamDriver to build a list of children based on the children from the cursor.
-        mStreamDriver.getLeafFeatureDrivers();
-
-        mStreamDriver.triggerPendingDismiss(contentId, undoAction, mPendingDismissCallback);
-
-        verify(mContentlistener).notifyContentRemoved(0);
-        verify(mSnackbarApi)
-                .show(eq(undoAction.getConfirmationLabel()),
-                        undoAction.hasUndoLabel()
-                                ? eq(undoAction.getUndoLabel())
-                                : eq(mContext.getResources().getString(R.string.undo)),
-                        snackbarCallbackApi.capture());
-
-        return snackbarCallbackApi.getValue();
-    }
-
-    private class StreamDriverForTest extends StreamDriver {
-        // TODO: create a fake for ContinuationDriver so that this can be
-        // List<FakeFeatureDriver>
-        private List<FeatureDriver> mChildrenCreated;
-        private boolean mWasRestoringDuringLastContinuationDriver;
-
-        StreamDriverForTest(ModelProvider modelProvider, ThreadUtils threadUtils, boolean restoring,
-                boolean isInitialLoad, UiRefreshReason uiRefreshReason) {
-            super(mActionApi, mActionManager, mActionParserFactory, modelProvider, threadUtils,
-                    mClock, mConfiguration, mContext, mSnackbarApi, mContentChangedListener,
-                    mScrollRestorer, mBasicLoggingApi, mStreamOfflineMonitor, mFeedKnownContent,
-                    mock(ContextMenuManager.class), restoring, isInitialLoad, mMainThreadRunner,
-                    mViewLoggingUpdater, mTooltipApi, uiRefreshReason, mScrollmonitor);
-            mChildrenCreated = new ArrayList<>();
-        }
-
-        @Override
-        ContinuationDriver createContinuationDriver(BasicLoggingApi basicLoggingApi, Clock clock,
-                Configuration configuration, Context context, ModelChild modelChild,
-                ModelProvider modelProvider, int position, SnackbarApi snackbarApi,
-                boolean restoring) {
-            this.mWasRestoringDuringLastContinuationDriver = restoring;
-            mChildrenCreated.add(mContinuationDriver);
-            return mContinuationDriver;
-        }
-
-        @Override
-        FeatureDriver createClusterDriver(ModelFeature modelFeature, int position) {
-            FeatureDriver featureDriver =
-                    new FakeFeatureDriver.Builder().setModelFeature(modelFeature).build();
-            mChildrenCreated.add(featureDriver);
-            return featureDriver;
-        }
-
-        @Override
-        FeatureDriver createCardDriver(ModelFeature modelFeature, int position) {
-            if (modelFeature != UNBOUND_CARD) {
-                FeatureDriver featureDriver =
-                        new FakeFeatureDriver.Builder().setModelFeature(modelFeature).build();
-                mChildrenCreated.add(featureDriver);
-                return featureDriver;
-            } else {
-                FeatureDriver featureDriver = new FakeFeatureDriver.Builder()
-                                                      .setModelFeature(modelFeature)
-                                                      .setLeafFeatureDriver(null)
-                                                      .build();
-                mChildrenCreated.add(featureDriver);
-                return featureDriver;
-            }
-        }
-
-        @Override
-        NoContentDriver createNoContentDriver() {
-            return mNoContentDriver;
-        }
-
-        @Override
-        ZeroStateDriver createZeroStateDriver() {
-            return mZeroStateDriver;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ZeroStateDriverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ZeroStateDriverTest.java
deleted file mode 100644
index 0053e5d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/ZeroStateDriverTest.java
+++ /dev/null
@@ -1,272 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.drivers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.host.logging.SpinnerType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.ZeroStateViewHolder;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.SpinnerLogger;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.UiRefreshReasonProto.UiRefreshReason.Reason;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ZeroStateDriver}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ZeroStateDriverTest {
-    private static final UiContext ZERO_STATE_UI_CONTEXT =
-            UiContext.newBuilder()
-                    .setExtension(UiRefreshReason.uiRefreshReasonExtension,
-                            UiRefreshReason.newBuilder().setReason(Reason.ZERO_STATE).build())
-                    .build();
-
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private SpinnerLogger mSpinnerLogger;
-    @Mock
-    private ZeroStateViewHolder mZeroStateViewHolder;
-
-    private ZeroStateDriver mZeroStateDriver;
-    private Clock mClock;
-    private Context mContext;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mClock = new FakeClock();
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ false);
-    }
-
-    @Test
-    public void testBind() {
-        assertThat(mZeroStateDriver.isBound()).isFalse();
-
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        assertThat(mZeroStateDriver.isBound()).isTrue();
-        verify(mZeroStateViewHolder).bind(mZeroStateDriver, /* showSpinner= */ false);
-    }
-
-    @Test
-    public void testBind_whenSpinnerShownTrue() {
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ true);
-
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        verify(mZeroStateViewHolder).bind(mZeroStateDriver, /* showSpinner= */ true);
-    }
-
-    @Test
-    public void testBindAfterOnClick_bindsViewholderWithSpinnerShown() {
-        InOrder inOrder = Mockito.inOrder(mZeroStateViewHolder);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        inOrder.verify(mZeroStateViewHolder).bind(mZeroStateDriver, /* showSpinner= */ false);
-
-        mZeroStateDriver.onClick(new FrameLayout(mContext));
-        mZeroStateDriver.unbind();
-
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        inOrder.verify(mZeroStateViewHolder).bind(mZeroStateDriver, /* showSpinner= */ true);
-    }
-
-    @Test
-    public void testBind_startsRecordingSpinner_ifSpinnerIsShownAndNotLogged() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(false);
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ true);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        verify(mSpinnerLogger).spinnerStarted(SpinnerType.INITIAL_LOAD);
-    }
-
-    @Test
-    public void testBind_doesNotStartRecordingSpinner_ifSpinnerIsNotShownAndNotLogged() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(false);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        verifyNoMoreInteractions(mSpinnerLogger);
-    }
-
-    @Test
-    public void testBind_doesNotStartRecordingSpinner_ifSpinnerIsShownAndAlreadyLogged() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(true);
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ true);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        verify(mSpinnerLogger, never()).spinnerStarted(anyInt());
-    }
-
-    @Test
-    public void testUnbind() {
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        assertThat(mZeroStateDriver.isBound()).isTrue();
-
-        mZeroStateDriver.unbind();
-
-        assertThat(mZeroStateDriver.isBound()).isFalse();
-        verify(mZeroStateViewHolder).unbind();
-    }
-
-    @Test
-    public void testUnbind_doesNotCallUnbindIfNotBound() {
-        assertThat(mZeroStateDriver.isBound()).isFalse();
-
-        mZeroStateDriver.unbind();
-        verifyNoMoreInteractions(mZeroStateViewHolder);
-    }
-
-    @Test
-    public void testOnClick() {
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        mZeroStateDriver.onClick(new FrameLayout(mContext));
-
-        verify(mZeroStateViewHolder).showSpinner(true);
-        verify(mContentChangedListener).onContentChanged();
-        verify(mModelProvider).triggerRefresh(RequestReason.ZERO_STATE, ZERO_STATE_UI_CONTEXT);
-    }
-
-    @Test
-    public void testOnClick_usesNewModelProvider_afterSettingNewModelProvider() {
-        ModelProvider newModelProvider = mock(ModelProvider.class);
-        mZeroStateDriver.setModelProvider(newModelProvider);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        mZeroStateDriver.onClick(new FrameLayout(mContext));
-
-        verify(newModelProvider).triggerRefresh(RequestReason.ZERO_STATE, ZERO_STATE_UI_CONTEXT);
-    }
-
-    @Test
-    public void testOnClick_startsRecordingSpinner_ifSpinnerNotActive() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(false);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        mZeroStateDriver.onClick(new FrameLayout(mContext));
-
-        verify(mSpinnerLogger).spinnerStarted(SpinnerType.ZERO_STATE_REFRESH);
-    }
-
-    @Test
-    public void testOnClick_throwsRuntimeException_ifSpinnerActive() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(true);
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ true);
-
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        reset(mSpinnerLogger);
-
-        assertThatRunnable(() -> mZeroStateDriver.onClick(new FrameLayout(mContext)));
-    }
-
-    @Test
-    public void testOnDestroy_logsSpinnerFinished_ifSpinnerActive() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(true);
-        mZeroStateDriver = new ZeroStateDriverForTest(mBasicLoggingApi, mClock, mModelProvider,
-                mContentChangedListener,
-                /* spinnerShown= */ true);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        mZeroStateDriver.onDestroy();
-
-        verify(mSpinnerLogger).spinnerFinished();
-    }
-
-    @Test
-    public void testOnDestroy_doesNotLogSpinnerFinished_ifSpinnerNotActive() {
-        when(mSpinnerLogger.isSpinnerActive()).thenReturn(false);
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-
-        mZeroStateDriver.onDestroy();
-        verify(mSpinnerLogger, never()).spinnerFinished();
-    }
-
-    @Test
-    public void testMaybeRebind() {
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        assertThat(mZeroStateDriver.isBound()).isTrue();
-        mZeroStateDriver.maybeRebind();
-        assertThat(mZeroStateDriver.isBound()).isTrue();
-        verify(mZeroStateViewHolder, times(2)).bind(mZeroStateDriver, /* showSpinner= */ false);
-        verify(mZeroStateViewHolder).unbind();
-    }
-
-    @Test
-    public void testMaybeRebind_nullViewHolder() {
-        mZeroStateDriver.bind(mZeroStateViewHolder);
-        mZeroStateDriver.unbind();
-        reset(mZeroStateViewHolder);
-
-        assertThat(mZeroStateDriver.isBound()).isFalse();
-        mZeroStateDriver.maybeRebind();
-        assertThat(mZeroStateDriver.isBound()).isFalse();
-        verify(mZeroStateViewHolder, never()).bind(any(OnClickListener.class), anyBoolean());
-        verify(mZeroStateViewHolder, never()).unbind();
-    }
-
-    private final class ZeroStateDriverForTest extends ZeroStateDriver {
-        ZeroStateDriverForTest(BasicLoggingApi basicLoggingApi, Clock clock,
-                ModelProvider modelProvider, ContentChangedListener contentChangedListener,
-                boolean spinnerShown) {
-            super(basicLoggingApi, clock, modelProvider, contentChangedListener, spinnerShown);
-        }
-
-        @Override
-        SpinnerLogger createSpinnerLogger(BasicLoggingApi basicLoggingApi, Clock clock) {
-            return mSpinnerLogger;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollMonitorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollMonitorTest.java
deleted file mode 100644
index e6ded422..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollMonitorTest.java
+++ /dev/null
@@ -1,137 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.scroll;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObserver;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link BasicStreamScrollMonitor}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class BasicStreamScrollMonitorTest {
-    private static final String FEATURE_ID = "";
-    private static final long TIMESTAMP = 150000L;
-
-    @Mock
-    private ScrollObserver mScrollObserver1;
-    @Mock
-    private ScrollObserver mScrollObserver2;
-
-    private RecyclerView mView;
-    private FakeClock mClock;
-    private BasicStreamScrollMonitor mScrollMonitor;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mView = new RecyclerView(Robolectric.buildActivity(Activity.class).get());
-        mClock = new FakeClock();
-        mClock.set(TIMESTAMP);
-        mScrollMonitor = new BasicStreamScrollMonitor(mClock);
-    }
-
-    @Test
-    public void testCallbacks_onScroll() {
-        mScrollMonitor.onScrolled(mView, 0, 0);
-
-        mScrollMonitor.addScrollObserver(mScrollObserver1);
-
-        mScrollMonitor.onScrolled(mView, 1, 1);
-        mScrollMonitor.onScrolled(mView, 2, 2);
-
-        mScrollMonitor.addScrollObserver(mScrollObserver2);
-
-        mScrollMonitor.onScrolled(mView, 3, 3);
-        mScrollMonitor.onScrolled(mView, 4, 4);
-
-        mScrollMonitor.removeScrollObserver(mScrollObserver1);
-
-        mScrollMonitor.onScrolled(mView, 5, 5);
-        mScrollMonitor.onScrolled(mView, 6, 6);
-
-        InOrder inOrder1 = inOrder(mScrollObserver1);
-        inOrder1.verify(mScrollObserver1).onScroll(mView, FEATURE_ID, 1, 1);
-        inOrder1.verify(mScrollObserver1).onScroll(mView, FEATURE_ID, 2, 2);
-        inOrder1.verify(mScrollObserver1).onScroll(mView, FEATURE_ID, 3, 3);
-        inOrder1.verify(mScrollObserver1).onScroll(mView, FEATURE_ID, 4, 4);
-
-        InOrder inOrder2 = inOrder(mScrollObserver2);
-        inOrder2.verify(mScrollObserver2).onScroll(mView, FEATURE_ID, 3, 3);
-        inOrder2.verify(mScrollObserver2).onScroll(mView, FEATURE_ID, 4, 4);
-        inOrder2.verify(mScrollObserver2).onScroll(mView, FEATURE_ID, 5, 5);
-        inOrder2.verify(mScrollObserver2).onScroll(mView, FEATURE_ID, 6, 6);
-    }
-
-    @Test
-    public void testCallbacks_onScrollStateChanged() {
-        mScrollMonitor.onScrollStateChanged(mView, 0);
-
-        mScrollMonitor.addScrollObserver(mScrollObserver1);
-
-        mScrollMonitor.onScrollStateChanged(mView, 1);
-
-        mScrollMonitor.addScrollObserver(mScrollObserver2);
-
-        mScrollMonitor.onScrollStateChanged(mView, 2);
-
-        mScrollMonitor.removeScrollObserver(mScrollObserver1);
-
-        mScrollMonitor.onScrollStateChanged(mView, 1);
-        mScrollMonitor.onScrollStateChanged(mView, 0);
-
-        InOrder inOrder1 = inOrder(mScrollObserver1);
-        inOrder1.verify(mScrollObserver1).onScrollStateChanged(mView, FEATURE_ID, 1, TIMESTAMP);
-        inOrder1.verify(mScrollObserver1).onScrollStateChanged(mView, FEATURE_ID, 2, TIMESTAMP);
-
-        InOrder inOrder2 = inOrder(mScrollObserver2);
-        inOrder2.verify(mScrollObserver2).onScrollStateChanged(mView, FEATURE_ID, 2, TIMESTAMP);
-        inOrder2.verify(mScrollObserver2).onScrollStateChanged(mView, FEATURE_ID, 1, TIMESTAMP);
-        inOrder2.verify(mScrollObserver2).onScrollStateChanged(mView, FEATURE_ID, 0, TIMESTAMP);
-    }
-
-    @Test
-    public void testCallbacks_afterEvent() {
-        mScrollMonitor.onScrolled(mView, 1, 1);
-        mScrollMonitor.onScrolled(mView, 2, 2);
-
-        mScrollMonitor.addScrollObserver(mScrollObserver1);
-
-        verify(mScrollObserver1, never())
-                .onScroll(any(View.class), anyString(), anyInt(), anyInt());
-    }
-
-    @Test
-    public void testGetCurrentScrollState() {
-        assertThat(mScrollMonitor.getCurrentScrollState())
-                .isEqualTo(RecyclerView.SCROLL_STATE_IDLE);
-        mScrollMonitor.onScrollStateChanged(mView, RecyclerView.SCROLL_STATE_DRAGGING);
-        assertThat(mScrollMonitor.getCurrentScrollState())
-                .isEqualTo(RecyclerView.SCROLL_STATE_DRAGGING);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollTrackerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollTrackerTest.java
deleted file mode 100644
index 0df70b9..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/BasicStreamScrollTrackerTest.java
+++ /dev/null
@@ -1,54 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.scroll;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.ScrollType;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObservable;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObserver;
-import org.chromium.chrome.browser.feed.library.sharedstream.scroll.ScrollLogger;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link BasicStreamScrollTracker}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class BasicStreamScrollTrackerTest {
-    @Mock
-    private ScrollLogger mLogger;
-    @Mock
-    private ScrollObservable mScrollObservable;
-
-    private BasicStreamScrollTracker mScrollTracker;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mScrollTracker = new BasicStreamScrollTracker(mLogger, mScrollObservable);
-        verify(mScrollObservable).addScrollObserver(any(ScrollObserver.class));
-    }
-
-    @Test
-    public void onUnbind_removedScrollListener() {
-        mScrollTracker.onUnbind();
-        verify(mScrollObservable).removeScrollObserver(any(ScrollObserver.class));
-    }
-
-    @Test
-    public void onScrollEvent() {
-        int scrollAmount = 10;
-        long timestamp = 10L;
-        mScrollTracker.onScrollEvent(scrollAmount);
-        verify(mLogger).handleScroll(ScrollType.STREAM_SCROLL, scrollAmount);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/ScrollRestorerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/ScrollRestorerTest.java
deleted file mode 100644
index 486704a8..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/scroll/ScrollRestorerTest.java
+++ /dev/null
@@ -1,191 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.scroll;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.ScrollRestorer.nonRestoringRestorer;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.sharedstream.scroll.ScrollListenerNotifier;
-import org.chromium.chrome.browser.feed.library.testing.android.LinearLayoutManagerForTest;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.ScrollStateProto.ScrollState;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ScrollRestorer}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ScrollRestorerTest {
-    private static final int HEADER_COUNT = 3;
-    private static final int TOP_POSITION = 4;
-    private static final int BOTTOM_POSITION = 10;
-    private static final int ABANDON_RESTORE_THRESHOLD = BOTTOM_POSITION;
-    private static final int OFFSET = -10;
-    private static final ScrollState SCROLL_STATE =
-            ScrollState.newBuilder().setPosition(TOP_POSITION).setOffset(OFFSET).build();
-
-    @Mock
-    private ScrollListenerNotifier mScrollListenerNotifier;
-    @Mock
-    private Configuration mConfiguration;
-
-    private RecyclerView mRecyclerView;
-    private LinearLayoutManagerForTest mLayoutManager;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        when(mConfiguration.getValueOrDefault(
-                     eq(ConfigKey.ABANDON_RESTORE_BELOW_FOLD), anyBoolean()))
-                .thenReturn(false);
-        when(mConfiguration.getValueOrDefault(
-                     eq(ConfigKey.ABANDON_RESTORE_BELOW_FOLD_THRESHOLD), anyLong()))
-                .thenReturn((long) ABANDON_RESTORE_THRESHOLD);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        mRecyclerView = new RecyclerView(mContext);
-        mLayoutManager = new LinearLayoutManagerForTest(mContext);
-        mRecyclerView.setLayoutManager(mLayoutManager);
-    }
-
-    @Test
-    public void testRestoreScroll() {
-        ScrollRestorer scrollRestorer = new ScrollRestorer(
-                mConfiguration, mRecyclerView, mScrollListenerNotifier, SCROLL_STATE);
-        scrollRestorer.maybeRestoreScroll();
-
-        verify(mScrollListenerNotifier).onProgrammaticScroll(mRecyclerView);
-        assertThat(mLayoutManager.scrolledPosition).isEqualTo(TOP_POSITION);
-        assertThat(mLayoutManager.scrolledOffset).isEqualTo(OFFSET);
-    }
-
-    @Test
-    public void testRestoreScroll_repeatedCall() {
-        ScrollRestorer scrollRestorer = new ScrollRestorer(
-                mConfiguration, mRecyclerView, mScrollListenerNotifier, createScrollState(10, 20));
-        scrollRestorer.maybeRestoreScroll();
-
-        mLayoutManager.scrollToPositionWithOffset(TOP_POSITION, OFFSET);
-        scrollRestorer.maybeRestoreScroll();
-
-        verify(mScrollListenerNotifier).onProgrammaticScroll(mRecyclerView);
-        assertThat(mLayoutManager.scrolledPosition).isEqualTo(TOP_POSITION);
-        assertThat(mLayoutManager.scrolledOffset).isEqualTo(OFFSET);
-    }
-
-    @Test
-    public void testAbandonRestoringScroll() {
-        mLayoutManager.scrollToPositionWithOffset(TOP_POSITION, OFFSET);
-
-        ScrollRestorer scrollRestorer = new ScrollRestorer(
-                mConfiguration, mRecyclerView, mScrollListenerNotifier, createScrollState(10, 20));
-        scrollRestorer.abandonRestoringScroll();
-        scrollRestorer.maybeRestoreScroll();
-
-        assertThat(mLayoutManager.scrolledPosition).isEqualTo(TOP_POSITION);
-        assertThat(mLayoutManager.scrolledOffset).isEqualTo(OFFSET);
-    }
-
-    @Test
-    public void testGetScrollStateForScrollPosition() {
-        mLayoutManager.firstVisibleItemPosition = TOP_POSITION;
-        mLayoutManager.firstVisibleViewOffset = OFFSET;
-        View view = new View(mContext);
-        view.setTop(OFFSET);
-        mLayoutManager.addChildToPosition(TOP_POSITION, view);
-        ScrollState restorerScrollState =
-                nonRestoringRestorer(mConfiguration, mRecyclerView, mScrollListenerNotifier)
-                        .getScrollStateForScrollRestore(HEADER_COUNT);
-        assertThat(restorerScrollState).isEqualTo(SCROLL_STATE);
-    }
-
-    @Test
-    public void testGetBundleForScrollPosition_invalidPosition() {
-        assertThat(nonRestoringRestorer(mConfiguration, mRecyclerView, mScrollListenerNotifier)
-                           .getScrollStateForScrollRestore(10))
-                .isNull();
-    }
-
-    @Test
-    public void testGetScrollStateForScrollPosition_abandonRestoreBelowFold_properRestore() {
-        when(mConfiguration.getValueOrDefault(
-                     eq(ConfigKey.ABANDON_RESTORE_BELOW_FOLD), anyBoolean()))
-                .thenReturn(true);
-
-        mLayoutManager.firstVisibleItemPosition = TOP_POSITION;
-        mLayoutManager.firstVisibleViewOffset = OFFSET;
-        mLayoutManager.lastVisibleItemPosition = BOTTOM_POSITION;
-        View view = new View(mContext);
-        view.setTop(OFFSET);
-        mLayoutManager.addChildToPosition(TOP_POSITION, view);
-        ScrollState restorerScrollState =
-                nonRestoringRestorer(mConfiguration, mRecyclerView, mScrollListenerNotifier)
-                        .getScrollStateForScrollRestore(HEADER_COUNT);
-        assertThat(restorerScrollState).isEqualTo(SCROLL_STATE);
-    }
-
-    @Test
-    public void testGetScrollStateForScrollPosition_abandonRestoreBelowFold_badBottomPosition() {
-        when(mConfiguration.getValueOrDefault(
-                     eq(ConfigKey.ABANDON_RESTORE_BELOW_FOLD), anyBoolean()))
-                .thenReturn(true);
-
-        mLayoutManager.firstVisibleItemPosition = TOP_POSITION;
-        mLayoutManager.firstVisibleViewOffset = OFFSET;
-        View view = new View(mContext);
-        view.setTop(OFFSET);
-        mLayoutManager.addChildToPosition(TOP_POSITION, view);
-        ScrollState restorerScrollState =
-                nonRestoringRestorer(mConfiguration, mRecyclerView, mScrollListenerNotifier)
-                        .getScrollStateForScrollRestore(HEADER_COUNT);
-        assertThat(restorerScrollState).isNull();
-    }
-
-    @Test
-    public void testGetScrollStateForScrollPosition_abandonRestoreBelowFold_restorePastThreshold() {
-        when(mConfiguration.getValueOrDefault(
-                     eq(ConfigKey.ABANDON_RESTORE_BELOW_FOLD), anyBoolean()))
-                .thenReturn(true);
-
-        mLayoutManager.firstVisibleItemPosition = TOP_POSITION;
-        mLayoutManager.firstVisibleViewOffset = OFFSET;
-        mLayoutManager.lastVisibleItemPosition = ABANDON_RESTORE_THRESHOLD + HEADER_COUNT + 1;
-        View view = new View(mContext);
-        view.setTop(OFFSET);
-        mLayoutManager.addChildToPosition(TOP_POSITION, view);
-        ScrollState restorerScrollState =
-                nonRestoringRestorer(mConfiguration, mRecyclerView, mScrollListenerNotifier)
-                        .getScrollStateForScrollRestore(HEADER_COUNT);
-        assertThat(restorerScrollState).isNull();
-    }
-
-    private ScrollState createScrollState(int position, int offset) {
-        return ScrollState.newBuilder().setPosition(position).setOffset(offset).build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ContinuationViewHolderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ContinuationViewHolderTest.java
deleted file mode 100644
index 9f5f6bcd..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ContinuationViewHolderTest.java
+++ /dev/null
@@ -1,139 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.LoggingListener;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.VisibilityMonitor;
-import org.chromium.chrome.browser.feed.library.testing.host.stream.FakeCardConfiguration;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ContinuationViewHolder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContinuationViewHolderTest {
-    private static final Configuration CONFIGURATION = new Configuration.Builder().build();
-
-    private ContinuationViewHolder mContinuationViewHolder;
-    private Context mContext;
-    private FrameLayout mFrameLayout;
-    private CardConfiguration mCardConfiguration;
-
-    @Mock
-    private OnClickListener mOnClickListener;
-    @Mock
-    private LoggingListener mLoggingListener;
-    @Mock
-    private VisibilityMonitor mVisibilityMonitor;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mCardConfiguration = new FakeCardConfiguration();
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mContext.setTheme(R.style.Light);
-        mFrameLayout = new FrameLayout(mContext);
-        mFrameLayout.setLayoutParams(new MarginLayoutParams(100, 100));
-        mContinuationViewHolder = new ContinuationViewHolder(
-                CONFIGURATION, mContext, mFrameLayout, mCardConfiguration) {
-            @Override
-            VisibilityMonitor createVisibilityMonitor(View view, Configuration configuration) {
-                return mVisibilityMonitor;
-            }
-        };
-    }
-
-    @Test
-    public void testConstructorInflatesView() {
-        assertThat((View) mFrameLayout.findViewById(R.id.more_button)).isNotNull();
-    }
-
-    @Test
-    public void testClick() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ false);
-
-        View buttonView = mFrameLayout.findViewById(R.id.action_button);
-        buttonView.performClick();
-
-        verify(mOnClickListener).onClick(buttonView);
-        verify(mLoggingListener).onContentClicked();
-    }
-
-    @Test
-    public void testBind_spinnerOff() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ false);
-        View buttonView = mFrameLayout.findViewById(R.id.action_button);
-        View spinnerView = mFrameLayout.findViewById(R.id.loading_spinner);
-
-        assertThat(buttonView.isClickable()).isTrue();
-        assertThat(buttonView.getVisibility()).isEqualTo(View.VISIBLE);
-
-        assertThat(spinnerView.getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void testBind_spinnerOn() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ true);
-        View buttonView = mFrameLayout.findViewById(R.id.action_button);
-        View spinnerView = mFrameLayout.findViewById(R.id.loading_spinner);
-
-        assertThat(buttonView.getVisibility()).isEqualTo(View.GONE);
-        assertThat(spinnerView.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testBind_setsListener() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ false);
-
-        verify(mVisibilityMonitor).setListener(mLoggingListener);
-    }
-
-    @Test
-    public void testBind_setsMargins() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ false);
-
-        MarginLayoutParams marginLayoutParams =
-                (MarginLayoutParams) mContinuationViewHolder.itemView.getLayoutParams();
-        assertThat(marginLayoutParams.bottomMargin)
-                .isEqualTo(mCardConfiguration.getCardBottomMargin());
-        assertThat(marginLayoutParams.leftMargin)
-                .isEqualTo(mCardConfiguration.getCardStartMargin());
-        assertThat(marginLayoutParams.rightMargin).isEqualTo(mCardConfiguration.getCardEndMargin());
-        assertThat(marginLayoutParams.topMargin)
-                .isEqualTo((int) mContext.getResources().getDimension(
-                        R.dimen.feed_more_button_container_top_margins));
-    }
-
-    @Test
-    public void testUnbind() {
-        mContinuationViewHolder.bind(mOnClickListener, mLoggingListener, /* showSpinner= */ false);
-        mContinuationViewHolder.unbind();
-
-        View view = mFrameLayout.findViewById(R.id.action_button);
-        assertThat(view.isClickable()).isFalse();
-        verify(mVisibilityMonitor).setListener(null);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/HeaderViewHolderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/HeaderViewHolderTest.java
deleted file mode 100644
index 2c06ea5d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/HeaderViewHolderTest.java
+++ /dev/null
@@ -1,105 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.shared.stream.Header;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link HeaderViewHolder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HeaderViewHolderTest {
-    private Context mContext;
-    private HeaderViewHolder mHeaderViewHolder;
-    private FrameLayout mFrameLayout;
-    private FrameLayout mHeaderView;
-
-    @Mock
-    private Header mHeader;
-    @Mock
-    private SwipeNotifier mSwipeNotifier;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mFrameLayout = new FrameLayout(mContext);
-        mHeaderView = new FrameLayout(mContext);
-        mHeaderViewHolder = new HeaderViewHolder(mFrameLayout);
-        when(mHeader.getView()).thenReturn(mHeaderView);
-    }
-
-    @Test
-    public void testBind() {
-        mHeaderViewHolder.bind(mHeader, mSwipeNotifier);
-
-        assertThat(mFrameLayout.getChildAt(0)).isSameInstanceAs(mHeaderView);
-    }
-
-    @Test
-    public void testBind_removesHeaderFromPreviousView_ifHeaderViewAlreadyHasParent() {
-        FrameLayout parentView = new FrameLayout(mContext);
-        parentView.addView(mHeaderView);
-
-        mHeaderViewHolder.bind(mHeader, mSwipeNotifier);
-
-        assertThat(parentView.getChildCount()).isEqualTo(0);
-        assertThat(mFrameLayout.getChildAt(0)).isSameInstanceAs(mHeaderView);
-    }
-
-    @Test
-    public void testUnbind() {
-        mHeaderViewHolder.bind(mHeader, mSwipeNotifier);
-    }
-
-    @Test
-    public void testCanSwipe() {
-        when(mHeader.isDismissible()).thenReturn(true);
-        mHeaderViewHolder.bind(mHeader, mSwipeNotifier);
-
-        assertThat(mHeaderViewHolder.canSwipe()).isTrue();
-    }
-
-    @Test
-    public void testCanSwipe_returnsFalse_whenViewHolderNotBound() {
-        when(mHeader.isDismissible()).thenReturn(true);
-
-        assertThat(mHeaderViewHolder.canSwipe()).isFalse();
-    }
-
-    @Test
-    public void testOnSwiped() {
-        mHeaderViewHolder.bind(mHeader, mSwipeNotifier);
-
-        mHeaderViewHolder.onSwiped();
-
-        verify(mSwipeNotifier).onSwiped();
-    }
-
-    @Test
-    public void testOnSwiped_doesNotCallOnSwiped_whenViewHolderNotBound() {
-        mHeaderViewHolder.onSwiped();
-
-        verify(mSwipeNotifier, never()).onSwiped();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/NoContentViewHolderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/NoContentViewHolderTest.java
deleted file mode 100644
index b73c054f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/NoContentViewHolderTest.java
+++ /dev/null
@@ -1,80 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link NoContentViewHolder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class NoContentViewHolderTest {
-    private NoContentViewHolder mNoContentViewHolder;
-    private FrameLayout mFrameLayout;
-
-    ColorDrawable mRedBackground = new ColorDrawable(Color.RED);
-
-    @Mock
-    private CardConfiguration mCardConfiguration;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        context.setTheme(R.style.Light);
-        mFrameLayout = new FrameLayout(context);
-        mFrameLayout.setLayoutParams(new MarginLayoutParams(100, 100));
-        mNoContentViewHolder = new NoContentViewHolder(mCardConfiguration, context, mFrameLayout);
-        when(mCardConfiguration.getCardBackground()).thenReturn(mRedBackground);
-    }
-
-    @Test
-    public void testNoContentViewInflated() {
-        View view = mFrameLayout.findViewById(R.id.no_content);
-        assertThat(view).isNotNull();
-        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testBind_setsBackground() {
-        mNoContentViewHolder.bind();
-
-        assertThat(mFrameLayout.getBackground()).isEqualTo(mRedBackground);
-    }
-
-    @Test
-    public void testBind_setsMargins() {
-        mNoContentViewHolder.bind();
-
-        MarginLayoutParams marginLayoutParams =
-                (MarginLayoutParams) mNoContentViewHolder.itemView.getLayoutParams();
-        assertThat(marginLayoutParams.bottomMargin)
-                .isEqualTo(mCardConfiguration.getCardBottomMargin());
-        assertThat(marginLayoutParams.leftMargin)
-                .isEqualTo(mCardConfiguration.getCardStartMargin());
-        assertThat(marginLayoutParams.rightMargin).isEqualTo(mCardConfiguration.getCardEndMargin());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/PietViewHolderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/PietViewHolderTest.java
deleted file mode 100644
index e4a7776a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/PietViewHolderTest.java
+++ /dev/null
@@ -1,357 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.drawable.ColorDrawable;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionParser;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionSource;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionSourceConverter;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.actions.StreamActionApiImpl;
-import org.chromium.chrome.browser.feed.library.basicstream.internal.scroll.BasicStreamScrollMonitor;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.PietManager;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.piet.host.EventLogger;
-import org.chromium.chrome.browser.feed.library.piet.testing.FakeFrameAdapter;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.LoggingListener;
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.VisibilityMonitor;
-import org.chromium.chrome.browser.feed.library.sharedstream.piet.PietEventLogger;
-import org.chromium.chrome.browser.feed.library.testing.host.stream.FakeCardConfiguration;
-import org.chromium.chrome.R;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionPayloadProto.FeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link PietViewHolder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietViewHolderTest {
-    private static final Action ACTION = Action.getDefaultInstance();
-    private static final Frame FRAME = Frame.newBuilder().build();
-    private static final FeedActionPayload SWIPE_ACTION =
-            FeedActionPayload.newBuilder()
-                    .setExtension(FeedAction.feedActionExtension, FeedAction.getDefaultInstance())
-                    .build();
-    private static final LogData VE_LOGGING_INFO = LogData.getDefaultInstance();
-
-    private CardConfiguration mCardConfiguration;
-    @Mock
-    private ActionParser mActionParser;
-    @Mock
-    private PietManager mPietManager;
-    @Mock
-    private StreamActionApiImpl mStreamActionApi;
-    @Mock
-    private LoggingListener mLoggingListener;
-    @Mock
-    private VisibilityMonitor mVisibilityMonitor;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-
-    private BasicStreamScrollMonitor mStreamScrollMonitor;
-    private FakeFrameAdapter mFrameAdapter;
-    private ActionHandler mActionHandler;
-    private Configuration mConfiguration;
-    private PietViewHolder mPietViewHolder;
-    private FrameLayout mFrameLayout;
-    private View mView;
-    private View mViewport;
-    private final List<PietSharedState> mPietSharedStates = new ArrayList<>();
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mStreamScrollMonitor = new BasicStreamScrollMonitor(new FakeClock());
-        mCardConfiguration = new FakeCardConfiguration();
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mFrameLayout = new FrameLayout(mContext);
-        mFrameLayout.setLayoutParams(new MarginLayoutParams(100, 100));
-        mView = new TextView(mContext);
-        mViewport = new FrameLayout(mContext);
-
-        // TODO: Use FakePietManager once it is implemented.
-        when(mPietManager.createPietFrameAdapter(ArgumentMatchers.<Supplier<ViewGroup>>any(),
-                     any(ActionHandler.class), any(EventLogger.class), eq(mContext)))
-                .thenAnswer(invocation -> {
-                    mFrameAdapter =
-                            FakeFrameAdapter.builder(mContext)
-                                    .setActionHandler((ActionHandler) invocation.getArguments()[1])
-                                    .addViewAction(Action.getDefaultInstance())
-                                    .addHideAction(Action.getDefaultInstance())
-                                    .build();
-                    return mFrameAdapter;
-                });
-
-        mConfiguration = new Configuration.Builder().build();
-        mPietViewHolder = new PietViewHolder(mCardConfiguration, mFrameLayout, mPietManager,
-                mStreamScrollMonitor, mViewport, mContext, mConfiguration,
-                new PietEventLogger(mBasicLoggingApi)) {
-            @Override
-            VisibilityMonitor createVisibilityMonitor(View view, Configuration configuration) {
-                return mVisibilityMonitor;
-            }
-        };
-
-        ArgumentCaptor<ActionHandler> captor = ArgumentCaptor.forClass(ActionHandler.class);
-        verify(mPietManager)
-                .createPietFrameAdapter(ArgumentMatchers.<Supplier<ViewGroup>>any(),
-                        captor.capture(), any(EventLogger.class), any(Context.class));
-        mActionHandler = captor.getValue();
-    }
-
-    @Test
-    public void testCardViewSetup() {
-        assertThat(mFrameLayout.getId()).isEqualTo(R.id.feed_content_card);
-    }
-
-    @Test
-    public void testBind_clearsPadding() {
-        mFrameLayout.setPadding(1, 2, 3, 4);
-
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        assertThat(mFrameLayout.getPaddingLeft()).isEqualTo(0);
-        assertThat(mFrameLayout.getPaddingRight()).isEqualTo(0);
-        assertThat(mFrameLayout.getPaddingTop()).isEqualTo(0);
-        assertThat(mFrameLayout.getPaddingBottom()).isEqualTo(0);
-    }
-
-    @Test
-    public void testBind_setsBackground() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        assertThat(((ColorDrawable) mFrameLayout.getBackground()).getColor())
-                .isEqualTo(((ColorDrawable) mCardConfiguration.getCardBackground()).getColor());
-    }
-
-    @Test
-    public void testBind_setsMargins() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        MarginLayoutParams marginLayoutParams =
-                (MarginLayoutParams) mPietViewHolder.itemView.getLayoutParams();
-        assertThat(marginLayoutParams.bottomMargin)
-                .isEqualTo(mCardConfiguration.getCardBottomMargin());
-        assertThat(marginLayoutParams.leftMargin)
-                .isEqualTo(mCardConfiguration.getCardStartMargin());
-        assertThat(marginLayoutParams.rightMargin).isEqualTo(mCardConfiguration.getCardEndMargin());
-    }
-
-    @Test
-    public void testBind_bindsModel() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        assertThat(mFrameAdapter.isBound()).isTrue();
-    }
-
-    @Test
-    public void testBind_onlyBindsOnce() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        // Should not crash.
-    }
-
-    @Test
-    public void testBind_setsListener() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        verify(mVisibilityMonitor).setListener(mLoggingListener);
-    }
-
-    @Test
-    public void testBind_setsScrollListener() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        assertThat(mStreamScrollMonitor.getObserverCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void testUnbind() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mPietViewHolder.unbind();
-
-        assertThat(mFrameAdapter.isBound()).isFalse();
-    }
-
-    @Test
-    public void testUnbind_setsListenerToNull() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mPietViewHolder.unbind();
-
-        verify(mVisibilityMonitor).setListener(null);
-    }
-
-    @Test
-    public void testUnbind_unsetsScrollListener() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mPietViewHolder.unbind();
-
-        assertThat(mStreamScrollMonitor.getObserverCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testSwipe_cantSwipeWithDefaultInstance() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        assertThat(mPietViewHolder.canSwipe()).isFalse();
-    }
-
-    @Test
-    public void testSwipe_canSwipeWithNonDefaultInstance() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                SWIPE_ACTION, mLoggingListener, mActionParser);
-
-        assertThat(mPietViewHolder.canSwipe()).isTrue();
-    }
-
-    @Test
-    public void testSwipeToDismissPerformed() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                SWIPE_ACTION, mLoggingListener, mActionParser);
-
-        mPietViewHolder.onSwiped();
-
-        verify(mLoggingListener).onContentSwiped();
-        verify(mActionParser)
-                .parseFeedActionPayload(SWIPE_ACTION, mStreamActionApi, mPietViewHolder.itemView,
-                        ActionSource.SWIPE);
-    }
-
-    @Test
-    public void testActionHandler_logsClickOnClickAction() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mActionHandler.handleAction(ACTION, ActionType.CLICK, FRAME, mView, VE_LOGGING_INFO);
-
-        verify(mLoggingListener).onContentClicked();
-        verify(mActionParser)
-                .parseAction(ACTION, mStreamActionApi, mView, VE_LOGGING_INFO, ActionSource.CLICK);
-    }
-
-    @Test
-    public void testActionHandler_doesNotLogClickOnLongClickAction() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mActionHandler.handleAction(ACTION, ActionType.LONG_CLICK, FRAME, mView, VE_LOGGING_INFO);
-
-        verify(mLoggingListener, never()).onContentClicked();
-        verify(mActionParser)
-                .parseAction(
-                        ACTION, mStreamActionApi, mView, VE_LOGGING_INFO, ActionSource.LONG_CLICK);
-    }
-
-    @Test
-    public void testActionHandler_doesNotLogClickOnViewAction() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        mActionHandler.handleAction(ACTION, ActionType.VIEW, FRAME, mView, VE_LOGGING_INFO);
-
-        verify(mLoggingListener, never()).onContentClicked();
-        verify(mActionParser)
-                .parseAction(ACTION, mStreamActionApi, mView, VE_LOGGING_INFO, ActionSource.VIEW);
-    }
-
-    @Test
-    public void testHideActionsOnUnbind() {
-        mPietViewHolder.bind(Frame.getDefaultInstance(), mPietSharedStates, mStreamActionApi,
-                FeedActionPayload.getDefaultInstance(), mLoggingListener, mActionParser);
-
-        // Triggers view actions.
-        mStreamScrollMonitor.onScrollStateChanged(
-                new RecyclerView(mContext), RecyclerView.SCROLL_STATE_IDLE);
-
-        // Triggers hide actions associated with those views
-        mPietViewHolder.unbind();
-
-        // TODO: Instead of using the default instance twice, create an extension for test
-        // proto.
-        // Once on the view, once on the hide.
-        verify(mActionParser, times(2))
-                .parseAction(Action.getDefaultInstance(), mStreamActionApi,
-                        mFrameAdapter.getFrameContainer(), LogData.getDefaultInstance(),
-                        ActionSourceConverter.convertPietAction(ActionType.VIEW));
-    }
-
-    @Test
-    public void testPietError_loggedToHost() {
-        ArgumentCaptor<EventLogger> pietEventLoggerCaptor =
-                ArgumentCaptor.forClass(EventLogger.class);
-
-        verify(mPietManager)
-                .createPietFrameAdapter(ArgumentMatchers.<Supplier<ViewGroup>>any(),
-                        eq(mActionHandler), pietEventLoggerCaptor.capture(), eq(mContext));
-
-        pietEventLoggerCaptor.getValue().logEvents(
-                Collections.singletonList(ErrorCode.ERR_MISSING_BINDING_VALUE));
-
-        verify(mBasicLoggingApi)
-                .onPietFrameRenderingEvent(
-                        Collections.singletonList(ErrorCode.ERR_MISSING_BINDING_VALUE.getNumber()));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ZeroStateViewHolderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ZeroStateViewHolderTest.java
deleted file mode 100644
index 139374db..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewholders/ZeroStateViewHolderTest.java
+++ /dev/null
@@ -1,149 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.testing.host.stream.FakeCardConfiguration;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ZeroStateViewHolder}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ZeroStateViewHolderTest {
-    @Mock
-    private OnClickListener mOnClickListener;
-
-    private CardConfiguration mCardConfiguration;
-    private FrameLayout mFrameLayout;
-    private ZeroStateViewHolder mZeroStateViewHolder;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mCardConfiguration = new FakeCardConfiguration();
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        context.setTheme(R.style.Light);
-        mFrameLayout = new FrameLayout(context);
-        mFrameLayout.setLayoutParams(new MarginLayoutParams(100, 100));
-        mZeroStateViewHolder = new ZeroStateViewHolder(context, mFrameLayout, mCardConfiguration);
-    }
-
-    @Test
-    public void testZeroStateViewInflated() {
-        View view = mFrameLayout.findViewById(R.id.zero_state);
-        assertThat(view).isNotNull();
-        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testBind_setsOnClickListener() {
-        mZeroStateViewHolder.bind(mOnClickListener, /* showSpinner= */ false);
-
-        View actionButton = mFrameLayout.findViewById(R.id.action_button);
-        actionButton.performClick();
-
-        verify(mOnClickListener).onClick(actionButton);
-    }
-
-    @Test
-    public void testBind_showsLoadingSpinner_whenInitializingFalse() {
-        mZeroStateViewHolder.bind(mOnClickListener, /* showSpinner= */ false);
-
-        View spinner = mFrameLayout.findViewById(R.id.loading_spinner);
-        View zeroState = mFrameLayout.findViewById(R.id.zero_state);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.GONE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testBind_hidesSpinner() {
-        View spinner = mFrameLayout.findViewById(R.id.loading_spinner);
-        View zeroState = mFrameLayout.findViewById(R.id.zero_state);
-        mZeroStateViewHolder.showSpinner(true);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.GONE);
-
-        mZeroStateViewHolder.bind(mOnClickListener, /* showSpinner= */ false);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.GONE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testBind_setsMargins() {
-        mZeroStateViewHolder.bind(mOnClickListener, /* showSpinner= */ false);
-
-        View zeroStateView = mZeroStateViewHolder.itemView.findViewById(R.id.no_content_container);
-        MarginLayoutParams marginLayoutParams =
-                (MarginLayoutParams) zeroStateView.getLayoutParams();
-        assertThat(marginLayoutParams.bottomMargin)
-                .isEqualTo(mCardConfiguration.getCardBottomMargin());
-        assertThat(marginLayoutParams.leftMargin)
-                .isEqualTo(mCardConfiguration.getCardStartMargin());
-        assertThat(marginLayoutParams.rightMargin).isEqualTo(mCardConfiguration.getCardEndMargin());
-    }
-
-    @Test
-    public void testUnbind() {
-        mZeroStateViewHolder.bind(mOnClickListener, /* showSpinner= */ false);
-
-        View actionButton = mFrameLayout.findViewById(R.id.action_button);
-        assertThat(actionButton.hasOnClickListeners()).isTrue();
-        assertThat(actionButton.isClickable()).isTrue();
-
-        mZeroStateViewHolder.unbind();
-
-        assertThat(actionButton.hasOnClickListeners()).isFalse();
-        assertThat(actionButton.isClickable()).isFalse();
-    }
-
-    @Test
-    public void testShowSpinner_showsSpinner() {
-        View spinner = mFrameLayout.findViewById(R.id.loading_spinner);
-        View zeroState = mFrameLayout.findViewById(R.id.zero_state);
-
-        mZeroStateViewHolder.showSpinner(true);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void testShowSpinner_hidesSpinner() {
-        View spinner = mFrameLayout.findViewById(R.id.loading_spinner);
-        View zeroState = mFrameLayout.findViewById(R.id.zero_state);
-        mZeroStateViewHolder.showSpinner(true);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.GONE);
-
-        mZeroStateViewHolder.showSpinner(false);
-
-        assertThat(spinner.getVisibility()).isEqualTo(View.GONE);
-        assertThat(zeroState.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ResettableOneShotVisibilityLoggingListenerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ResettableOneShotVisibilityLoggingListenerTest.java
deleted file mode 100644
index 919337f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ResettableOneShotVisibilityLoggingListenerTest.java
+++ /dev/null
@@ -1,34 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.sharedstream.logging.LoggingListener;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ResettableOneShotVisibilityLoggingListener}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ResettableOneShotVisibilityLoggingListenerTest {
-    @Test
-    public void testReset_logsTwice() {
-        LoggingListener loggingListener = mock(LoggingListener.class);
-        ResettableOneShotVisibilityLoggingListener resettableLoggingListener =
-                new ResettableOneShotVisibilityLoggingListener(loggingListener);
-        resettableLoggingListener.onViewVisible();
-        resettableLoggingListener.reset();
-        resettableLoggingListener.onViewVisible();
-        resettableLoggingListener.onViewVisible();
-
-        verify(loggingListener, times(2)).onViewVisible();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ViewLoggingUpdaterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ViewLoggingUpdaterTest.java
deleted file mode 100644
index 2d6646cf..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/basicstream/internal/viewloggingupdater/ViewLoggingUpdaterTest.java
+++ /dev/null
@@ -1,37 +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.
-
-package org.chromium.chrome.browser.feed.library.basicstream.internal.viewloggingupdater;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ViewLoggingUpdater}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ViewLoggingUpdaterTest {
-    @Test
-    public void testResetViewTracking_resetsListeners() {
-        ResettableOneShotVisibilityLoggingListener listerner1 =
-                mock(ResettableOneShotVisibilityLoggingListener.class);
-        ResettableOneShotVisibilityLoggingListener listerner2 =
-                mock(ResettableOneShotVisibilityLoggingListener.class);
-
-        ViewLoggingUpdater viewLoggingUpdater = new ViewLoggingUpdater();
-
-        viewLoggingUpdater.registerObserver(listerner1);
-        viewLoggingUpdater.registerObserver(listerner2);
-
-        viewLoggingUpdater.resetViewTracking();
-
-        verify(listerner1).reset();
-        verify(listerner2).reset();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ResultTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ResultTest.java
deleted file mode 100644
index 25e308c60..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ResultTest.java
+++ /dev/null
@@ -1,43 +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.
-
-package org.chromium.chrome.browser.feed.library.common;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test class for {@link Result} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ResultTest {
-    private static final String HELLO_WORLD = "Hello World";
-
-    @Test
-    public void success() throws Exception {
-        Result<String> result = Result.success(HELLO_WORLD);
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).isEqualTo(HELLO_WORLD);
-    }
-
-    @Test
-    public void success_nullValue() throws Exception {
-        Result<String> result = Result.success(null);
-        assertThat(result.isSuccessful()).isTrue();
-        assertThatRunnable(result::getValue).throwsAnExceptionOfType(NullPointerException.class);
-    }
-
-    @Test
-    public void error() throws Exception {
-        Result<String> result = Result.failure();
-        assertThat(result.isSuccessful()).isFalse();
-        assertThatRunnable(result::getValue).throwsAnExceptionOfType(IllegalStateException.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ValidatorsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ValidatorsTest.java
deleted file mode 100644
index 3dd2012..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ValidatorsTest.java
+++ /dev/null
@@ -1,66 +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.
-
-package org.chromium.chrome.browser.feed.library.common;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link Validators} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ValidatorsTest {
-    @Test
-    public void testCheckNotNull_null() {
-        assertThatRunnable(() -> Validators.checkNotNull(null))
-                .throwsAnExceptionOfType(NullPointerException.class);
-    }
-
-    @Test
-    public void testCheckNotNull_debugString() {
-        String debugString = "Debug-String";
-        assertThatRunnable(() -> Validators.checkNotNull(null, debugString))
-                .throwsAnExceptionOfType(NullPointerException.class)
-                .that()
-                .hasMessageThat()
-                .contains(debugString);
-    }
-
-    @Test
-    public void testCheckNotNull_notNull() {
-        String test = "test";
-        assertThat(Validators.checkNotNull(test)).isEqualTo(test);
-    }
-
-    @Test
-    public void testCheckState_trueWithMessagel() {
-        // no exception expected
-        Validators.checkState(true, "format-string");
-    }
-
-    @Test
-    public void testCheckState_True() {
-        // no exception expected
-        Validators.checkState(true);
-    }
-
-    @Test
-    public void testCheckState_falseWithMessage() {
-        assertThatRunnable(() -> Validators.checkState(false, "format-string"))
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-
-    @Test
-    public void testCheckState_false() {
-        assertThatRunnable(() -> Validators.checkState(false))
-                .throwsAnExceptionOfType(IllegalStateException.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/CancelableRunnableTaskTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/CancelableRunnableTaskTest.java
deleted file mode 100644
index 390212a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/CancelableRunnableTaskTest.java
+++ /dev/null
@@ -1,42 +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.
-
-package org.chromium.chrome.browser.feed.library.common.concurrent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link TaskQueue} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CancelableRunnableTaskTest {
-    private boolean mWasRun;
-    private CancelableRunnableTask mCancelable;
-
-    @Before
-    public void setUp() {
-        mCancelable = new CancelableRunnableTask(() -> { mWasRun = true; });
-    }
-
-    @Test
-    public void testRunnableCanceled_notCanceled() {
-        mCancelable.run();
-
-        assertThat(mWasRun).isTrue();
-    }
-
-    @Test
-    public void testRunnableCanceled_canceled() {
-        mCancelable.cancel();
-        mCancelable.run();
-
-        assertThat(mWasRun).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/SimpleSettableFutureTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/SimpleSettableFutureTest.java
deleted file mode 100644
index 7a6c7bb7..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/SimpleSettableFutureTest.java
+++ /dev/null
@@ -1,174 +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.
-
-package org.chromium.chrome.browser.feed.library.common.concurrent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/** Tests for {@link SimpleSettableFuture} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SimpleSettableFutureTest {
-    @Test
-    public void testCancel() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        boolean cancelled = future.cancel(false);
-        assertThat(cancelled).isTrue();
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(CancellationException.class);
-        assertThat(future.isCancelled()).isTrue();
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testCancel_multipleCalls() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        boolean cancelled = future.cancel(false);
-        cancelled = future.cancel(false);
-        assertThat(cancelled).isTrue();
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(CancellationException.class);
-        assertThat(future.isCancelled()).isTrue();
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testCancel_alreadyFinished() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.put(Boolean.TRUE);
-        boolean cancelled = future.cancel(false);
-        assertThat(cancelled).isFalse();
-    }
-
-    @Test
-    public void testPut() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.put(Boolean.TRUE);
-        Boolean futureResult = future.get();
-        assertThat(futureResult).isEqualTo(Boolean.TRUE);
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testPut_onlyOnce() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.put(Boolean.TRUE);
-        future.put(Boolean.FALSE);
-        Boolean futureResult = future.get();
-        assertThat(futureResult).isEqualTo(Boolean.TRUE);
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testPut_onlyOnce_andException() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.put(Boolean.TRUE);
-        future.putException(new RuntimeException());
-        Boolean futureResult = future.get();
-        assertThat(futureResult).isEqualTo(Boolean.TRUE);
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testPut_alreadyCancelled() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        boolean cancelled = future.cancel(false);
-        future.put(Boolean.TRUE);
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(CancellationException.class);
-        assertThat(future.isCancelled()).isTrue();
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testPutException() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.putException(new RuntimeException());
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testPutException_onlyOnce() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.putException(new RuntimeException());
-        future.putException(new IllegalAccessException());
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testPutException_onlyOnce_andValue() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.putException(new RuntimeException());
-        future.put(Boolean.TRUE);
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testPutException_alreadyCancelled() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        boolean cancelled = future.cancel(false);
-        future.putException(new RuntimeException());
-        assertThatRunnable(future::get)
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(CancellationException.class);
-        assertThat(future.isCancelled()).isTrue();
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testGetTimeout() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        assertThatRunnable(() -> future.get(1, TimeUnit.MILLISECONDS))
-                .throwsAnExceptionOfType(TimeoutException.class);
-    }
-
-    @Test
-    public void testGetTimeout_success() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.put(Boolean.TRUE);
-        Boolean futureResult = future.get(1, TimeUnit.MILLISECONDS);
-        assertThat(futureResult).isEqualTo(Boolean.TRUE);
-        assertThat(future.isDone()).isTrue();
-    }
-
-    @Test
-    public void testGetTimeout_cancelled() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.cancel(false);
-        assertThatRunnable(() -> future.get(1, TimeUnit.MILLISECONDS))
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(CancellationException.class);
-    }
-
-    @Test
-    public void testGetTimeout_putException() throws Exception {
-        SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
-        future.putException(new RuntimeException());
-        assertThatRunnable(() -> future.get(1, TimeUnit.MILLISECONDS))
-                .throwsAnExceptionOfType(ExecutionException.class)
-                .causedByAnExceptionOfType(RuntimeException.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/TaskQueueTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/TaskQueueTest.java
deleted file mode 100644
index 12b722f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/concurrent/TaskQueueTest.java
+++ /dev/null
@@ -1,328 +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.
-
-package org.chromium.chrome.browser.feed.library.common.concurrent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.logging.Task;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue.TaskType;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link TaskQueue} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TaskQueueTest {
-    private static final long LONG_RUNNING_TASK_TIME = 123456L;
-    private final FakeBasicLoggingApi mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final List<Integer> mCallOrder = new ArrayList<>();
-    private final FakeMainThreadRunner mFakeMainThreadRunner =
-            FakeMainThreadRunner.create(mFakeClock);
-    private final TaskQueue mTaskQueue = new TaskQueue(mFakeBasicLoggingApi,
-            MoreExecutors.directExecutor(), mFakeMainThreadRunner, mFakeClock);
-
-    private boolean mDelayedTaskHasRun;
-    private boolean mDelayedTaskHasTimedOut;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mCallOrder.clear();
-    }
-
-    @Test
-    public void testInitialization() {
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-    }
-
-    @Test
-    public void testInitializationCallOrder() {
-        Runnable background = () -> runnable(1);
-        Runnable userFacing = () -> runnable(2);
-        Runnable postInit = () -> runnable(3);
-        Runnable init = () -> runnable(4);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, background);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.USER_FACING, userFacing);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.IMMEDIATE, postInit);
-        assertThat(mTaskQueue.hasBacklog()).isTrue();
-        mTaskQueue.initialize(init);
-
-        assertThat(mTaskQueue.hasBacklog()).isFalse();
-        assertOrder(4, 3, 2, 1);
-    }
-
-    @Test
-    public void testPostInit() {
-        mTaskQueue.initialize(this::noOp);
-
-        Runnable invalidate = () -> runnable(1);
-        Runnable postInit = () -> runnable(2);
-        Runnable userFacing = () -> runnable(3);
-        Runnable reset = () -> runnable(4);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, invalidate);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.IMMEDIATE, postInit);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.USER_FACING, userFacing);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_RESET, reset);
-
-        assertThat(mTaskQueue.hasBacklog()).isFalse();
-        assertOrder(1, 2, 4, 3);
-    }
-
-    @Test
-    public void testHeadInvalidateReset() {
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask);
-
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-        assertThat(mDelayedTaskHasRun).isFalse();
-        assertThat(mTaskQueue.hasPendingStarvationCheck()).isTrue();
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_RESET, this::noOp);
-
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-        assertThat(mDelayedTaskHasRun).isTrue();
-        assertThat(mTaskQueue.hasPendingStarvationCheck()).isFalse();
-    }
-
-    @Test
-    public void testHeadInvalidateDropMultiple() {
-        Runnable invalidate = () -> runnable(1);
-        Runnable invalidateNotRun = () -> runnable(2);
-        Runnable init = () -> runnable(3);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, invalidate);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, invalidateNotRun);
-        assertThat(mTaskQueue.hasBacklog()).isTrue();
-        mTaskQueue.initialize(init);
-
-        assertThat(mTaskQueue.hasBacklog()).isFalse();
-        assertOrder(3, 1);
-    }
-
-    @Test
-    public void testHeadInvalidateCallOrder() {
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-
-        Runnable headInvalidate = () -> runnable(1);
-        Runnable background = () -> runnable(2);
-        Runnable userFacing = () -> runnable(3);
-        Runnable postInit = () -> runnable(4);
-        Runnable headReset = () -> runnable(5);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, headInvalidate);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, background);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.USER_FACING, userFacing);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.IMMEDIATE, postInit); // run immediately
-        assertThat(mTaskQueue.hasBacklog()).isTrue();
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_RESET, headReset);
-
-        assertThat(mTaskQueue.hasBacklog()).isFalse();
-        assertOrder(1, 4, 5, 3, 2);
-    }
-
-    @Test
-    public void testResetTaskQueue_immediate() {
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-
-        mTaskQueue.reset();
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-    }
-
-    @Test
-    public void testResetQueue_withDelay() {
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-
-        Runnable headInvalidate = () -> runnable(1);
-        Runnable background = () -> runnable(2);
-        Runnable userFacing = () -> runnable(3);
-        Runnable postInit = () -> runnable(4);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, headInvalidate);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, background);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.USER_FACING, userFacing);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.IMMEDIATE, postInit);
-        assertThat(mTaskQueue.hasBacklog()).isTrue();
-
-        mTaskQueue.reset();
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-        assertThat(mTaskQueue.hasBacklog()).isFalse();
-        mTaskQueue.initialize(this::noOp);
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-    }
-
-    @Test
-    public void testStarvation_initialization() {
-        mTaskQueue.initialize(this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask);
-
-        assertThat(mDelayedTaskHasRun).isTrue();
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-    }
-
-    @Test
-    public void testStarvation_headInvalidate() {
-        mTaskQueue.initialize(this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask);
-
-        assertThat(mFakeMainThreadRunner.getCompletedTaskCount()).isEqualTo(0);
-        assertThat(mDelayedTaskHasRun).isFalse();
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-
-        runAndAssertStarvationChecks();
-        assertThat(mDelayedTaskHasRun).isTrue();
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-        assertThat(mFakeBasicLoggingApi.lastInternalError)
-                .isEqualTo(InternalFeedError.TASK_QUEUE_STARVATION);
-    }
-
-    @Test
-    public void testStarvation_reset() {
-        mTaskQueue.initialize(this::noOp);
-        mTaskQueue.reset();
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask);
-
-        assertThat(mDelayedTaskHasRun).isFalse();
-        assertThat(mTaskQueue.isDelayed()).isTrue();
-
-        runAndAssertStarvationChecks();
-        assertThat(mDelayedTaskHasRun).isTrue();
-        assertThat(mTaskQueue.isDelayed()).isFalse();
-    }
-
-    @Test
-    public void testTaskTiming_immediatelyRunTask_logsTaskFinished() {
-        mTaskQueue.initialize(this::noOp);
-
-        mTaskQueue.execute(Task.CLEAR_ALL, TaskType.IMMEDIATE, this::noOp);
-
-        assertThat(mFakeBasicLoggingApi.lastTaskDelay).isEqualTo(0);
-        assertThat(mFakeBasicLoggingApi.lastTaskTime).isEqualTo(0);
-        assertThat(mFakeBasicLoggingApi.lastTaskLogged).isEqualTo(Task.CLEAR_ALL);
-    }
-
-    @Test
-    public void testTaskTiming_longTask_logsTaskFinished() {
-        mTaskQueue.initialize(this::noOp);
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.IMMEDIATE, this::longRunningTask);
-
-        assertThat(mFakeBasicLoggingApi.lastTaskTime).isEqualTo(LONG_RUNNING_TASK_TIME);
-    }
-
-    @Test
-    public void testTaskTiming_delayedTask_logsTaskFinished() {
-        // Queue task before initialization, causing it to be delayed
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, this::delayedTask);
-        long delayTime = 123L;
-        mFakeClock.advance(delayTime);
-
-        // Initialize, allowing the delayed task to be run
-        mTaskQueue.initialize(this::noOp);
-
-        assertThat(mFakeBasicLoggingApi.lastTaskDelay).isEqualTo(delayTime);
-    }
-
-    @Test
-    public void testTimeout_withoutTimeout() {
-        // Put the TaskQueue into a delaying state and schedule a task with a timeout.
-        mTaskQueue.initialize(this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask,
-                this::delayedTaskTimeout,
-                /*timeoutMillis= */ 10L);
-
-        mFakeClock.advance(9L);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_RESET, this::noOp);
-        assertThat(mDelayedTaskHasRun).isTrue();
-        assertThat(mDelayedTaskHasTimedOut).isFalse();
-
-        mFakeClock.tick();
-        assertThat(mDelayedTaskHasTimedOut).isFalse();
-    }
-
-    @Test
-    public void testTimeout_withTimeout() {
-        // Put the TaskQueue into a delaying state and schedule a task with a timeout.
-        mTaskQueue.initialize(this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_INVALIDATE, this::noOp);
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, this::delayedTask,
-                this::delayedTaskTimeout,
-                /*timeoutMillis= */ 10L);
-
-        mFakeClock.advance(10L);
-        assertThat(mDelayedTaskHasRun).isFalse();
-        assertThat(mDelayedTaskHasTimedOut).isTrue();
-
-        mTaskQueue.execute(Task.UNKNOWN, TaskType.HEAD_RESET, this::noOp);
-        assertThat(mDelayedTaskHasRun).isFalse();
-    }
-
-    private void runAndAssertStarvationChecks() {
-        int starvationTaskCount = 0;
-        long startTimeMillis = mFakeClock.currentTimeMillis();
-        while (mFakeClock.currentTimeMillis() - startTimeMillis < TaskQueue.STARVATION_TIMEOUT_MS) {
-            assertThat(mTaskQueue.hasPendingStarvationCheck()).isTrue();
-            mFakeClock.advance(TaskQueue.STARVATION_CHECK_MS);
-            starvationTaskCount++;
-            assertThat(mFakeMainThreadRunner.getCompletedTaskCount())
-                    .isEqualTo(starvationTaskCount);
-        }
-        assertThat(mTaskQueue.hasPendingStarvationCheck()).isFalse();
-    }
-
-    private void assertOrder(Integer... args) {
-        assertThat(mCallOrder.size()).isEqualTo(args.length);
-        for (int i = 0; i < args.length; i++) {
-            assertThat(mCallOrder.get(i)).isEqualTo(args[i]);
-        }
-    }
-
-    private void runnable(int taskId) {
-        mCallOrder.add(taskId);
-    }
-
-    private void noOp() {}
-
-    private void delayedTask() {
-        mDelayedTaskHasRun = true;
-    }
-
-    private void delayedTaskTimeout() {
-        mDelayedTaskHasTimedOut = true;
-    }
-
-    private void longRunningTask() {
-        mFakeClock.advance(LONG_RUNNING_TASK_TIME);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/feedobservable/FeedObservableTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/feedobservable/FeedObservableTest.java
deleted file mode 100644
index 4d6de17..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/feedobservable/FeedObservableTest.java
+++ /dev/null
@@ -1,95 +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.
-
-package org.chromium.chrome.browser.feed.library.common.feedobservable;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests for {@link org.chromium.chrome.browser.feed.library.common.feedobservable.FeedObservable}.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedObservableTest {
-    private FeedObservableForTest mObservable;
-
-    @Before
-    public void setup() {
-        mObservable = new FeedObservableForTest();
-    }
-
-    @Test
-    public void updateObservers_updatesRegisteredObservers() {
-        Observer observer = new Observer();
-        mObservable.registerObserver(observer);
-
-        mObservable.updateObservers();
-
-        assertThat(observer.mUpdateCount).isEqualTo(1);
-    }
-
-    @Test
-    public void updateObservers_doesntUpdateUnregisteredObservers() {
-        Observer observer = new Observer();
-        mObservable.registerObserver(observer);
-        mObservable.unregisterObserver(observer);
-
-        mObservable.updateObservers();
-
-        assertThat(observer.mUpdateCount).isEqualTo(0);
-    }
-
-    @Test
-    public void updateObservers_multipleObservers_updatesAll() {
-        Observer observer1 = new Observer();
-        Observer observer2 = new Observer();
-        mObservable.registerObserver(observer1);
-        mObservable.registerObserver(observer2);
-
-        mObservable.updateObservers();
-
-        assertThat(observer1.mUpdateCount).isEqualTo(1);
-        assertThat(observer2.mUpdateCount).isEqualTo(1);
-    }
-
-    @Test
-    public void updateObservers_doubleRegister_updatesOnce() {
-        Observer observer = new Observer();
-        mObservable.registerObserver(observer);
-        mObservable.registerObserver(observer);
-
-        mObservable.updateObservers();
-
-        assertThat(observer.mUpdateCount).isEqualTo(1);
-    }
-
-    @Test
-    public void unRegisterObserver_nonRegisteredObserver_doesntCrash() {
-        // Should not crash.
-        mObservable.unregisterObserver(new Observer());
-    }
-
-    private static class FeedObservableForTest extends FeedObservable<Observer> {
-        void updateObservers() {
-            for (Observer observer : mObservers) {
-                observer.update();
-            }
-        }
-    }
-
-    private static class Observer {
-        private int mUpdateCount;
-
-        void update() {
-            mUpdateCount++;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/HashPoolInternerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/HashPoolInternerTest.java
deleted file mode 100644
index 0518318..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/HashPoolInternerTest.java
+++ /dev/null
@@ -1,98 +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.
-
-package org.chromium.chrome.browser.feed.library.common.intern;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheets;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link HashPoolInterner} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HashPoolInternerTest {
-    private final HashPoolInterner<PietSharedStateWrapper> mInterner = new HashPoolInterner<>();
-
-    @Before
-    public void setUp() throws Exception {}
-
-    @Test
-    public void testBasic() {
-        PietSharedStateWrapper first = new PietSharedStateWrapper(
-                PietSharedState.newBuilder()
-                        .addTemplates(Template.newBuilder().setTemplateId("foo").setStylesheets(
-                                Stylesheets.newBuilder().addStylesheetIds("bar")))
-                        .addStylesheets(Stylesheet.newBuilder().setStylesheetId("baz"))
-                        .build());
-        PietSharedStateWrapper second = new PietSharedStateWrapper(
-                PietSharedState.newBuilder()
-                        .addTemplates(Template.newBuilder().setTemplateId("foo").setStylesheets(
-                                Stylesheets.newBuilder().addStylesheetIds("bar")))
-                        .addStylesheets(Stylesheet.newBuilder().setStylesheetId("baz"))
-                        .build());
-        PietSharedStateWrapper third = new PietSharedStateWrapper(
-                PietSharedState.newBuilder()
-                        .addTemplates(Template.newBuilder().setTemplateId("foo").setStylesheets(
-                                Stylesheets.newBuilder().addStylesheetIds("bar")))
-                        .addStylesheets(Stylesheet.newBuilder().setStylesheetId("bay"))
-                        .build());
-        assertThat(first).isNotSameInstanceAs(second);
-        assertThat(first).isNotSameInstanceAs(third);
-        assertThat(second).isNotSameInstanceAs(third);
-
-        // Pool is empty so first is added/returned.
-        PietSharedStateWrapper internedFirst = mInterner.intern(first);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internedFirst).isSameInstanceAs(first);
-
-        // Pool already has an identical proto, which is returned.
-        PietSharedStateWrapper internSecond = mInterner.intern(second);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internSecond).isSameInstanceAs(first);
-
-        // Third is a new object (not equal with any previous) so it is added to the pool.
-        PietSharedStateWrapper internedThird = mInterner.intern(third);
-        assertThat(mInterner.size()).isEqualTo(2);
-        assertThat(internedThird).isSameInstanceAs(third);
-
-        mInterner.clear();
-        assertThat(mInterner.size()).isEqualTo(0);
-
-        // Pool is empty so second is added/returned.
-        internSecond = mInterner.intern(second);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internSecond).isSameInstanceAs(second);
-    }
-
-    private static class PietSharedStateWrapper {
-        private final PietSharedState mPietSharedState;
-
-        private PietSharedStateWrapper(PietSharedState pietSharedState) {
-            this.mPietSharedState = pietSharedState;
-        }
-
-        @Override
-        public int hashCode() {
-            return mPietSharedState.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // Equals should never be called with HashPoolInterner, it may be very expensive.
-            fail();
-            return super.equals(obj);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternedMapTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternedMapTest.java
deleted file mode 100644
index 8351a79..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternedMapTest.java
+++ /dev/null
@@ -1,69 +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.
-
-package org.chromium.chrome.browser.feed.library.common.intern;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.HashMap;
-
-/** Tests of the {@link InternedMap} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InternedMapTest {
-    private final InternedMap<String, StreamStructure> mMap =
-            new InternedMap<>(new HashMap<>(), new WeakPoolInterner<>());
-
-    @Before
-    public void setUp() throws Exception {}
-
-    @Test
-    public void testBasic() {
-        StreamStructure first = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("bar")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        StreamStructure second = StreamStructure.newBuilder()
-                                         .setContentId("foo")
-                                         .setParentContentId("bar")
-                                         .setOperation(Operation.UPDATE_OR_APPEND)
-                                         .build();
-        StreamStructure third = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("baz")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        assertThat(first).isNotSameInstanceAs(second);
-        assertThat(first).isEqualTo(second);
-        assertThat(first).isNotEqualTo(third);
-
-        // Initially the interner pool is empty so first is added as value.
-        mMap.put("first", first);
-        assertThat(mMap.get("first")).isSameInstanceAs(first);
-
-        // The interner pool already has an identical value object so second is not added, first is
-        // used instead.
-        mMap.put("second", second);
-        assertThat(mMap.get("second")).isSameInstanceAs(first);
-
-        // Third is a new object (not equal with any previous) so it is added to the pool.
-        mMap.put("third", third);
-        assertThat(mMap.get("third")).isSameInstanceAs(third);
-
-        // Since the pool is empty, seconds is added to the pool this time.
-        mMap.clear();
-        mMap.put("second", second);
-        assertThat(mMap.get("second")).isSameInstanceAs(second);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternerWithStatsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternerWithStatsTest.java
deleted file mode 100644
index fa2f14ca..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/InternerWithStatsTest.java
+++ /dev/null
@@ -1,68 +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.
-
-package org.chromium.chrome.browser.feed.library.common.intern;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link InternerWithStats} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InternerWithStatsTest {
-    private final InternerWithStats<StreamStructure> mInterner =
-            new InternerWithStats<>(new WeakPoolInterner<>());
-
-    @Before
-    public void setUp() throws Exception {}
-
-    @Test
-    public void testBasic() {
-        StreamStructure first = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("bar")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        StreamStructure second = StreamStructure.newBuilder()
-                                         .setContentId("foo")
-                                         .setParentContentId("bar")
-                                         .setOperation(Operation.UPDATE_OR_APPEND)
-                                         .build();
-        StreamStructure third = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("baz")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        assertThat(first).isNotSameInstanceAs(second);
-        assertThat(first).isEqualTo(second);
-        assertThat(first).isNotEqualTo(third);
-
-        // Pool is empty so first is added/returned.
-        StreamStructure internedFirst = mInterner.intern(first);
-        assertThat(internedFirst).isSameInstanceAs(first);
-        assertThat(mInterner.getStats()).isEqualTo("Cache Hit Ratio: 0/1 (0.00)");
-
-        // Pool already has an identical proto, which is returned.
-        StreamStructure internedSecond = mInterner.intern(second);
-        assertThat(internedSecond).isSameInstanceAs(first);
-        assertThat(mInterner.getStats()).isEqualTo("Cache Hit Ratio: 1/2 (0.50)");
-
-        // Third is a new object (not equal with any previous) so it is added to the pool.
-        StreamStructure internedThird = mInterner.intern(third);
-        assertThat(internedThird).isSameInstanceAs(third);
-        assertThat(mInterner.getStats()).isEqualTo("Cache Hit Ratio: 1/3 (0.33)");
-
-        mInterner.clear();
-        assertThat(mInterner.size()).isEqualTo(0);
-        assertThat(mInterner.getStats()).isEqualTo("Cache Hit Ratio: 0/0 (1.00)");
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/WeakPoolInternerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/WeakPoolInternerTest.java
deleted file mode 100644
index b7ba026f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/intern/WeakPoolInternerTest.java
+++ /dev/null
@@ -1,67 +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.
-
-package org.chromium.chrome.browser.feed.library.common.intern;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link WeakPoolInterner} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class WeakPoolInternerTest {
-    private final WeakPoolInterner<StreamStructure> mInterner = new WeakPoolInterner<>();
-
-    @Test
-    public void testBasic() {
-        StreamStructure first = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("bar")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        StreamStructure second = StreamStructure.newBuilder()
-                                         .setContentId("foo")
-                                         .setParentContentId("bar")
-                                         .setOperation(Operation.UPDATE_OR_APPEND)
-                                         .build();
-        StreamStructure third = StreamStructure.newBuilder()
-                                        .setContentId("foo")
-                                        .setParentContentId("baz")
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        assertThat(first).isNotSameInstanceAs(second);
-        assertThat(first).isEqualTo(second);
-        assertThat(first).isNotEqualTo(third);
-
-        // Pool is empty so first is added/returned.
-        StreamStructure internedFirst = mInterner.intern(first);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internedFirst).isSameInstanceAs(first);
-
-        // Pool already has an identical proto, which is returned.
-        StreamStructure internedSecond = mInterner.intern(second);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internedSecond).isSameInstanceAs(first);
-
-        // Third is a new object (not equal with any previous) so it is added to the pool.
-        StreamStructure internedThird = mInterner.intern(third);
-        assertThat(mInterner.size()).isEqualTo(2);
-        assertThat(internedThird).isSameInstanceAs(third);
-
-        mInterner.clear();
-        assertThat(mInterner.size()).isEqualTo(0);
-
-        // Pool is empty so second is added/returned.
-        internedSecond = mInterner.intern(second);
-        assertThat(mInterner.size()).isEqualTo(1);
-        assertThat(internedSecond).isSameInstanceAs(second);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/locale/LocaleUtilsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/locale/LocaleUtilsTest.java
deleted file mode 100644
index c6b04c2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/locale/LocaleUtilsTest.java
+++ /dev/null
@@ -1,51 +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.
-
-package org.chromium.chrome.browser.feed.library.common.locale;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Build.VERSION_CODES;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Locale;
-
-/** Tests of the {@link LocaleUtils} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LocaleUtilsTest {
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = Robolectric.buildActivity(Activity.class).get();
-    }
-
-    @Test
-    @Config(qualifiers = "fr-rCA", sdk = VERSION_CODES.O /*N is not available*/)
-    public void getLocale_byContext_postN() {
-        assertThat(LocaleUtils.getLocale(mContext)).isEqualTo(Locale.CANADA_FRENCH);
-    }
-
-    @Test
-    @Config(qualifiers = "fr-rCA", sdk = VERSION_CODES.LOLLIPOP)
-    public void getLoca_byContext_preN() {
-        assertThat(LocaleUtils.getLocale(mContext)).isEqualTo(Locale.CANADA_FRENCH);
-    }
-
-    @Test
-    @Config(qualifiers = "fr-rCA")
-    public void getLanguageTag() {
-        assertThat(LocaleUtils.getLanguageTag(mContext)).isEqualTo("fr-CA");
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/DumperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/DumperTest.java
deleted file mode 100644
index c8094bf..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/DumperTest.java
+++ /dev/null
@@ -1,175 +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.
-
-package org.chromium.chrome.browser.feed.library.common.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.logging.Dumper.DumperValue;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.io.StringWriter;
-
-/** Tests of the {@link Dumper}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class DumperTest {
-    private static final String KEY1 = "keyOne";
-    private static final String KEY2 = "keyTwo";
-    private static final String KEY3 = "keyThree";
-    private static final String VALUE1 = "valueOne";
-    private static final String VALUE2 = "valueTwo";
-
-    @Test
-    public void testBaseDumper() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        dumper.forKey(KEY1).value(13);
-        assertThat(dumper.mValues).hasSize(1);
-        DumperValue dumperValue = dumper.mValues.get(0);
-        assertThat(dumperValue).isNotNull();
-        assertThat(dumperValue.mName).isEqualTo(KEY1);
-        assertThat(dumperValue.mContent.toString()).isEqualTo(Integer.toString(13));
-        assertThat(dumperValue.mIndentLevel).isEqualTo(1);
-    }
-
-    @Test
-    public void testBaseDumper_childDumper() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        dumper.forKey(KEY1).value(13);
-        assertThat(dumper.mValues).hasSize(1);
-
-        Dumper childDumper = dumper.getChildDumper();
-        childDumper.forKey(KEY2).value(17);
-
-        assertThat(childDumper.mValues).isEqualTo(dumper.mValues);
-        assertThat(childDumper.mValues).hasSize(2);
-
-        DumperValue dumperValue = dumper.mValues.get(1);
-        assertThat(dumperValue).isNotNull();
-        assertThat(dumperValue.mName).isEqualTo(KEY2);
-        assertThat(dumperValue.mContent.toString()).isEqualTo(Integer.toString(17));
-        assertThat(dumperValue.mIndentLevel).isEqualTo(2);
-    }
-
-    @Test
-    public void testDumpable() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        TestDumpable testDumpable = new TestDumpable(KEY1);
-        dumper.dump(testDumpable);
-        testDumpable.assertValue(dumper.mValues.get(0));
-    }
-
-    @Test
-    public void testDumpable_nested() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        TestDumpable dumpableChild = new TestDumpable(KEY1);
-        TestDumpable dumpable = new TestDumpable(dumpableChild, KEY2);
-        dumper.dump(dumpable);
-
-        assertThat(dumper.mValues).hasSize(2);
-        dumpable.assertValue(dumper.mValues.get(0));
-        dumpableChild.assertValue(dumper.mValues.get(1));
-    }
-
-    @Test
-    public void testDumpable_cycle() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        TestDumpable dumpableChild = new TestDumpable(KEY1);
-        TestDumpable dumpable = new TestDumpable(dumpableChild, KEY2);
-        dumpableChild.setDumpable(dumpable);
-
-        dumper.dump(dumpable);
-
-        // Two dumpables + the a message that statesa cycle was detected
-        assertThat(dumper.mValues).hasSize(3);
-        dumpable.assertValue(dumper.mValues.get(0));
-        dumpableChild.assertValue(dumper.mValues.get(1));
-    }
-
-    @Test
-    public void testDumpable_threeChildCycle() {
-        Dumper dumper = Dumper.newDefaultDumper();
-        TestDumpable dumpableThree = new TestDumpable(KEY1);
-        TestDumpable dumpableTwo = new TestDumpable(dumpableThree, KEY2);
-        TestDumpable dumpableOne = new TestDumpable(dumpableTwo, KEY3);
-        dumpableThree.setDumpable(dumpableOne);
-
-        dumper.dump(dumpableOne);
-
-        // 3 dumpables + the a message that states cycle was detected
-        assertThat(dumper.mValues).hasSize(4);
-    }
-
-    @Test
-    public void testWrite() throws Exception {
-        Dumper dumper = Dumper.newDefaultDumper();
-        TestDumpable testDumpable = new TestDumpable(KEY1);
-        dumper.dump(testDumpable);
-        StringWriter stringWriter = new StringWriter();
-        dumper.write(stringWriter);
-        String dump = stringWriter.toString();
-        assertThat(dump).contains(KEY1);
-        assertThat(dump).contains("13");
-    }
-
-    @Test
-    public void testWrite_compact() throws Exception {
-        Dumper dumper = Dumper.newDefaultDumper();
-        dumper.forKey(KEY1).value(VALUE1);
-        dumper.forKey(KEY2).value(VALUE2).compactPrevious();
-        StringWriter stringWriter = new StringWriter();
-        dumper.write(stringWriter);
-        String dump = stringWriter.toString();
-        assertThat(dump).contains(" | ");
-        assertThat(dump).contains(VALUE1);
-        assertThat(dump).contains(VALUE2);
-    }
-
-    @Test
-    public void testRedacted() throws Exception {
-        Dumper dumper = Dumper.newRedactedDumper();
-        dumper.forKey(KEY1).value(VALUE1).sensitive();
-        dumper.forKey(KEY2).value(VALUE2).compactPrevious();
-        StringWriter stringWriter = new StringWriter();
-        dumper.write(stringWriter);
-        String dump = stringWriter.toString();
-        assertThat(dump).contains(" | ");
-        assertThat(dump).contains(DumperValue.REDACTED);
-        assertThat(dump).doesNotContain(VALUE1);
-        assertThat(dump).contains(VALUE2);
-    }
-
-    private static class TestDumpable implements Dumpable {
-        /*@Nullable*/ private Dumpable mChild;
-        private final String mKey;
-
-        TestDumpable(String key) {
-            this(null, key);
-        }
-
-        TestDumpable(/*@Nullable*/ Dumpable child, String key) {
-            this.mChild = child;
-            this.mKey = key;
-        }
-
-        void setDumpable(Dumpable child) {
-            this.mChild = child;
-        }
-
-        @Override
-        public void dump(Dumper dumper) {
-            dumper.forKey(mKey).value(13);
-            dumper.dump(mChild);
-        }
-
-        void assertValue(DumperValue value) {
-            assertThat(value.mName).isEqualTo(mKey);
-            assertThat(value.mContent.toString()).isEqualTo(Integer.toString(13));
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/LoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/LoggerTest.java
deleted file mode 100644
index cc48e5e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/logging/LoggerTest.java
+++ /dev/null
@@ -1,102 +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.
-
-package org.chromium.chrome.browser.feed.library.common.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link Logger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LoggerTest {
-    private static final Object[] EMPTY_ARRAY = {};
-
-    @Test
-    public void testBuildMessage_emptyString() {
-        assertThat(Logger.buildMessage("", EMPTY_ARRAY)).isEmpty();
-    }
-
-    @Test
-    public void testBuildMessage_nullString() {
-        assertEquals("null", Logger.buildMessage(null, EMPTY_ARRAY));
-    }
-
-    @Test
-    public void testBuildMessage_nullStringWithArgs() {
-        // Test that any args are ignored
-        assertEquals("null", Logger.buildMessage(null, new Object[] {"A", "B", 2}));
-    }
-
-    @Test
-    public void testBuildMessage_noArgs() {
-        assertEquals("hello", Logger.buildMessage("hello", EMPTY_ARRAY));
-    }
-
-    @Test
-    public void testBuildMessage_formatString() {
-        String format = "%s scolded %s %d times? %b/%b";
-        assertEquals("A scolded B 2 times? true/false",
-                Logger.buildMessage(format, new Object[] {"A", "B", 2, true, false}));
-    }
-
-    @Test
-    public void testBuildMessage_formatStringForceArgsToString() {
-        String format = "%s scolded %s %s times? %s/%s";
-        assertEquals("A scolded B 2 times? true/false",
-                Logger.buildMessage(format, new Object[] {"A", "B", 2, true, false}));
-    }
-
-    @Test
-    public void testBuildMessage_formatStringNullArgs() {
-        String format = "%s scolded %s %d times? %b/%b";
-        assertEquals("null scolded null null times? false/false",
-                Logger.buildMessage(format, new Object[] {null, null, null, null, null}));
-    }
-
-    @Test
-    public void testBuildMessage_noFormat() {
-        String format = "Hello";
-        // Test that args are safely ignored
-        assertEquals("Hello", Logger.buildMessage(format, new Object[] {"A", "B", 2}));
-    }
-
-    @Test
-    public void testBuildMessage_illegalFormat() {
-        String format = "Hello %d";
-        // Test that args are concatenated when using a string for %d.
-        assertEquals("Hello %d[A]", Logger.buildMessage(format, new Object[] {"A"}));
-    }
-
-    @Test
-    public void testBuildMessage_arrayArgs() {
-        String format = "%s %s";
-        int[] a = {1, 2, 3};
-        Object[] b = {"a", new String[] {"b", "c"}};
-        assertEquals("[1, 2, 3] [a, [b, c]]", Logger.buildMessage(format, new Object[] {a, b}));
-    }
-
-    @Test
-    public void testBuildMessage_arrayArgsWithNull() {
-        String format = "%s";
-        Object a = new String[] {"a", null};
-        assertEquals("[a, null]", Logger.buildMessage(format, new Object[] {a}));
-    }
-
-    @Test
-    public void testBuildMessage_arrayArgsWithCycle() {
-        String format = "%s %s";
-        Object[] a = new Object[1];
-        Object[] b = {a};
-        a[0] = b;
-        assertEquals("[[[...]]] [[[...]]]", Logger.buildMessage(format, new Object[] {a, b}));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RequiredConsumerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RequiredConsumerTest.java
deleted file mode 100644
index 3bf9814..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RequiredConsumerTest.java
+++ /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.
-
-package org.chromium.chrome.browser.feed.library.common.testing;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.concurrent.CountDownLatch;
-
-/** Test class for {@link RequiredConsumer} */
-@RunWith(JUnit4.class)
-public class RequiredConsumerTest {
-    @Test
-    public void testConsumer() throws Exception {
-        CountDownLatch latch = new CountDownLatch(1);
-        Boolean callValue = Boolean.TRUE;
-
-        RequiredConsumer<Boolean> consumer = new RequiredConsumer<>(input -> {
-            assertThat(input).isEqualTo(callValue);
-            latch.countDown();
-        });
-
-        consumer.accept(callValue);
-
-        assertThat(latch.getCount()).isEqualTo(0);
-        assertThat(consumer.isCalled()).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RunnableSubjectTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RunnableSubjectTest.java
deleted file mode 100644
index 105d97f2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RunnableSubjectTest.java
+++ /dev/null
@@ -1,228 +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.
-
-package org.chromium.chrome.browser.feed.library.common.testing;
-
-import static com.google.common.truth.ExpectFailure.assertThat;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.runnables;
-
-import com.google.common.truth.ExpectFailure;
-import com.google.common.truth.Truth;
-
-import org.junit.ComparisonFailure;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.ThrowingRunnable;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
-
-/** Unit tests for {@link RunnableSubject}. */
-@RunWith(JUnit4.class)
-public class RunnableSubjectTest {
-    @Rule
-    public final ExpectFailure expectFailure = new ExpectFailure();
-
-    @Test
-    public void runToCompletion() throws Throwable {
-        ThrowingRunnable run = mock(ThrowingRunnable.class);
-
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(run)
-                .throwsAnExceptionOfType(RuntimeException.class);
-
-        verify(run).run();
-        AssertionError failure = expectFailure.getFailure();
-        assertThat(failure).factValue("expected to throw").isEqualTo("java.lang.RuntimeException");
-        assertThat(failure).factKeys().contains("but ran to completion");
-        verifyNoMoreInteractions(run);
-    }
-
-    @Test
-    public void nullSubjectFailsEvenWhenExpectingNullRefException() {
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(null)
-                .throwsAnExceptionOfType(NullPointerException.class);
-
-        AssertionError failure = expectFailure.getFailure();
-        assertThat(failure)
-                .factValue("expected to throw")
-                .isEqualTo("java.lang.NullPointerException");
-        assertThat(failure).factKeys().contains("but didn't run because it's null");
-    }
-
-    @Test
-    public void wrongException() {
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(() -> { throw new IllegalArgumentException("a"); })
-                .throwsAnExceptionOfType(NullPointerException.class);
-
-        AssertionError failure = expectFailure.getFailure();
-        assertThat(failure).factValue("value of").isEqualTo("runnable.thrownException()");
-        assertThat(failure)
-                .factValue("expected instance of")
-                .isEqualTo("java.lang.NullPointerException");
-        assertThat(failure)
-                .factValue("but was instance of")
-                .isEqualTo("java.lang.IllegalArgumentException");
-        assertThat(failure)
-                .factValue("with value")
-                .isEqualTo("java.lang.IllegalArgumentException: a");
-    }
-
-    @Test
-    public void correctException() {
-        IllegalArgumentException ex = new IllegalArgumentException("b");
-
-        Truth.assertAbout(runnables())
-                .that(() -> { throw ex; })
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .isSameInstanceAs(ex);
-    }
-
-    @Test
-    public void wrongMessage() {
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(() -> { throw new IOException("Wrong message!"); })
-                .throwsAnExceptionOfType(IOException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo("Expected message.");
-
-        ComparisonFailure failure = (ComparisonFailure) expectFailure.getFailure();
-        assertThat(failure.getExpected()).isEqualTo("Expected message.");
-        assertThat(failure.getActual()).isEqualTo("Wrong message!");
-    }
-
-    @Test
-    public void correctMessage() {
-        String expectedMessage = "Expected message.";
-
-        Truth.assertAbout(runnables())
-                .that(() -> { throw new IOException(expectedMessage); })
-                .throwsAnExceptionOfType(IOException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo(expectedMessage);
-    }
-
-    @Test
-    public void getCaught() {
-        IllegalArgumentException cause = new IllegalArgumentException("boo");
-        IOException ex = new IOException(cause);
-
-        IOException caught = RunnableSubject.assertThat(() -> { throw ex; })
-                                     .throwsAnExceptionOfType(IOException.class)
-                                     .getCaught();
-        assertThat(caught).isSameInstanceAs(ex);
-    }
-
-    @Test
-    public void wrongCauseType() {
-        IllegalArgumentException cause = new IllegalArgumentException("boo");
-        IOException ex = new IOException(cause);
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(() -> { throw ex; })
-                .throwsAnExceptionOfType(IOException.class)
-                .causedByAnExceptionOfType(ExecutionException.class);
-        AssertionError failure = expectFailure.getFailure();
-        assertThat(failure)
-                .factValue("value of")
-                .isEqualTo("runnable.thrownException().getCause()");
-        assertThat(failure)
-                .factValue("expected instance of")
-                .isEqualTo("java.util.concurrent.ExecutionException");
-        assertThat(failure)
-                .factValue("but was instance of")
-                .isEqualTo("java.lang.IllegalArgumentException");
-        assertThat(failure)
-                .factValue("with value")
-                .isEqualTo("java.lang.IllegalArgumentException: boo");
-    }
-
-    @Test
-    public void correctCauseType() {
-        IllegalArgumentException cause = new IllegalArgumentException("boo");
-        IOException ex = new IOException(cause);
-
-        Truth.assertAbout(runnables())
-                .that(() -> { throw ex; })
-                .throwsAnExceptionOfType(IOException.class)
-                .causedByAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo("boo");
-    }
-
-    @Test
-    public void causedByCorrect() {
-        IOException cause = new IOException();
-        IllegalArgumentException ex = new IllegalArgumentException(cause);
-
-        Truth.assertAbout(runnables())
-                .that(() -> { throw ex; })
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .causedBy(cause);
-    }
-
-    @Test
-    public void causedByWrong() {
-        IOException cause = new IOException();
-        IllegalArgumentException ex = new IllegalArgumentException(cause);
-
-        expectFailure.whenTesting()
-                .about(runnables())
-                .that(() -> { throw ex; })
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .causedBy(new IOException());
-    }
-
-    @Test
-    public void exampleUsage() {
-        assertThatRunnable(() -> { throw new IOException("boo!"); })
-                .throwsAnExceptionOfType(IOException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo("boo!");
-
-        RunnableSubject.assertThat(() -> { throw new IllegalArgumentException("oh no"); })
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo("oh no");
-
-        RunnableSubject
-                .assertThat(() -> {
-                    throw new IllegalArgumentException(new RuntimeException("glitch"));
-                })
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .causedByAnExceptionOfType(RuntimeException.class)
-                .that()
-                .hasMessageThat()
-                .isEqualTo("glitch");
-
-        // Recursive self-verification.
-        RunnableSubject
-                .assertThat(()
-                                    -> RunnableSubject.assertThat(() -> {}).throwsAnExceptionOfType(
-                                            RuntimeException.class))
-                .throwsAnExceptionOfType(AssertionError.class);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ui/LayoutUtilsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ui/LayoutUtilsTest.java
deleted file mode 100644
index 931503b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ui/LayoutUtilsTest.java
+++ /dev/null
@@ -1,65 +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.
-
-package org.chromium.chrome.browser.feed.library.common.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.widget.LinearLayout;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link LayoutUtils}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LayoutUtilsTest {
-    private static final int START = 1;
-    private static final int TOP = 2;
-    private static final int END = 3;
-    private static final int BOTTOM = 4;
-
-    private final Context mContext = ApplicationProvider.getApplicationContext();
-
-    @Test
-    public void testSetMarginsRelative() {
-        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
-        LayoutUtils.setMarginsRelative(lp, START, TOP, END, BOTTOM);
-        assertThat(lp.leftMargin).isEqualTo(START);
-        assertThat(lp.rightMargin).isEqualTo(END);
-        assertThat(lp.topMargin).isEqualTo(TOP);
-        assertThat(lp.bottomMargin).isEqualTo(BOTTOM);
-    }
-
-    @Test
-    public void testDpToPx() {
-        // TODO: Switch this to using @Config to set density to something different than 1.
-        assertThat(LayoutUtils.dpToPx(1000.0f, mContext)).isWithin(1.0e-04f).of(1000.0f);
-    }
-
-    @Test
-    public void testPxToDp() {
-        // TODO: Switch this to using @Config to set density to something different than 1.
-        assertThat(LayoutUtils.pxToDp(1000.0f, mContext)).isWithin(1.0e-04f).of(1000.0f);
-    }
-
-    @Test
-    public void testSpToPx() {
-        mContext.getResources().getDisplayMetrics().scaledDensity = 3.0f;
-        assertThat(LayoutUtils.spToPx(1000.0f, mContext)).isWithin(1.0e-03f).of(3000.0f);
-    }
-
-    @Test
-    public void testPxToSp() {
-        mContext.getResources().getDisplayMetrics().scaledDensity = 3.0f;
-        assertThat(LayoutUtils.pxToSp(3000.0f, mContext)).isWithin(1.0e-04f).of(1000.0f);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java
deleted file mode 100644
index d230c48..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java
+++ /dev/null
@@ -1,766 +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.
-package org.chromium.chrome.browser.feed.library.feedactionmanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.graphics.Rect;
-import android.view.View;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.api.internal.store.UploadableActionMutation;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.v1.FeedLoggingBridge;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamUploadableAction;
-import org.chromium.components.feed.core.proto.wire.ActionPayloadProto.ActionPayload;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.components.user_prefs.UserPrefsJni;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-/** Tests of the {@link FeedActionManagerImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-@Features.DisableFeatures({ChromeFeatureList.INTEREST_FEED_V2,
-        ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD})
-@Features.EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS,
-        ChromeFeatureList.REPORT_FEED_USER_ACTIONS})
-public class FeedActionManagerImplTest {
-    private static final String CONTENT_ID_STRING = "contentIdString";
-    private static final String SESSION_ID = "session";
-    private static final long DEFAULT_TIME = Duration.ofDays(42).toMillis();
-    private static final long DEFAULT_TIME_SECONDS = Duration.ofDays(42).getSeconds();
-    // View dimensions on screen. We satisfy or violate exposure and coverage (see
-    // FeedActionManagerImpl.VIEW_EXPOSURE_THRESHOLD and
-    // FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD for definitions and values). Dimensions are
-    // chosen according to the default exposure and coverage thresholds, and the size of the
-    // viewport.
-    private static final Rect VIEWPORT_RECT = new Rect(0, 0, 100, 100);
-    private static final Rect VISIBLE_RECT = new Rect(0, 0, 100, 50);
-    private static final Rect INVISIBLE_RECT = new Rect(0, -50, 100, 10);
-    // Durations for VIEW actions. Chosen according to the default VIEW duration threshold
-    // FeedActionManagerImpl.VIEW_DURATION_MS_THRESHOLD_DEFAULT.
-    private static final long LONG_DURATION_MS = 1000;
-    private static final long LONG_DURATION_S = Duration.ofMillis(LONG_DURATION_MS).toSeconds();
-    private static final long SHORT_DURATION_MS = 400;
-    private static final ActionPayload ACTION_PAYLOAD = ActionPayload.getDefaultInstance();
-
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeMainThreadRunner mFakeMainThreadRunner =
-            FakeMainThreadRunner.runTasksImmediately();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private Store mStore;
-    @Mock
-    private LocalActionMutation mLocalActionMutation;
-    @Mock
-    private UploadableActionMutation mUploadableActionMutation;
-    @Mock
-    private Consumer<Result<Model>> mModelConsumer;
-    @Mock
-    private FeedLoggingBridge mFeedLoggingBridge;
-    @Mock
-    private Runnable mStoreViewActionsRunnable;
-    @Mock
-    private UserPrefs.Natives mUserPrefsJniMock;
-    @Mock
-    private Profile mProfile;
-    @Mock
-    private PrefService mPrefService;
-
-    @Captor
-    private ArgumentCaptor<Integer> mActionTypeCaptor;
-    @Captor
-    private ArgumentCaptor<String> mContentIdStringCaptor;
-    @Captor
-    private ArgumentCaptor<Result<Model>> mModelCaptor;
-    @Captor
-    private ArgumentCaptor<MutationContext> mMutationContextCaptor;
-    @Captor
-    private ArgumentCaptor<Consumer<Result<ConsistencyToken>>> mConsumerCaptor;
-    @Captor
-    private ArgumentCaptor<Set<StreamUploadableAction>> mActionCaptor;
-    @Captor
-    private ArgumentCaptor<StreamUploadableAction> mUploadableActionCaptor;
-
-    private FeedActionManagerImpl mActionManager;
-
-    private TestView mViewport;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-
-        mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
-        Profile.setLastUsedProfileForTesting(mProfile);
-        when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService);
-
-        mActionManager = new FeedActionManagerImpl(mStore, mFakeThreadUtils, getTaskQueue(),
-                mFakeMainThreadRunner, new TestViewHandler(), mFakeClock, mFeedLoggingBridge);
-        mActionManager.initialize(mFeedSessionManager);
-
-        when(mFeedSessionManager.getUpdateConsumer(any(MutationContext.class)))
-                .thenReturn(mModelConsumer);
-
-        when(mLocalActionMutation.add(anyInt(), anyString())).thenReturn(mLocalActionMutation);
-        when(mStore.editLocalActions()).thenReturn(mLocalActionMutation);
-
-        when(mUploadableActionMutation.upsert(any(StreamUploadableAction.class), anyString()))
-                .thenReturn(mUploadableActionMutation);
-        when(mStore.editUploadableActions()).thenReturn(mUploadableActionMutation);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mViewport = new TestView();
-        mViewport.setRectOnScreen(VIEWPORT_RECT);
-        mActionManager.setViewport(mViewport);
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-    }
-
-    @Test
-    public void dismissLocal() throws Exception {
-        StreamDataOperation dataOperation = buildBasicDismissOperation();
-
-        mActionManager.dismissLocal(Collections.singletonList(CONTENT_ID_STRING),
-                Collections.singletonList(dataOperation), null);
-
-        verify(mLocalActionMutation)
-                .add(mActionTypeCaptor.capture(), mContentIdStringCaptor.capture());
-        assertThat(mActionTypeCaptor.getValue()).isEqualTo(ActionType.DISMISS);
-        assertThat(mContentIdStringCaptor.getValue()).isEqualTo(CONTENT_ID_STRING);
-
-        verify(mLocalActionMutation).commit();
-
-        verify(mModelConsumer).accept(mModelCaptor.capture());
-        Result<Model> result = mModelCaptor.getValue();
-        assertThat(result.isSuccessful()).isTrue();
-        List<StreamDataOperation> streamDataOperations = result.getValue().streamDataOperations;
-        assertThat(streamDataOperations).hasSize(1);
-        StreamDataOperation streamDataOperation = streamDataOperations.get(0);
-        assertThat(streamDataOperation).isEqualTo(dataOperation);
-    }
-
-    @Test
-    public void dismissLocal_sessionIdSet() throws Exception {
-        StreamDataOperation dataOperation = buildBasicDismissOperation();
-
-        mActionManager.dismissLocal(Collections.singletonList(CONTENT_ID_STRING),
-                Collections.singletonList(dataOperation), SESSION_ID);
-
-        verify(mLocalActionMutation)
-                .add(mActionTypeCaptor.capture(), mContentIdStringCaptor.capture());
-        assertThat(mActionTypeCaptor.getValue()).isEqualTo(ActionType.DISMISS);
-        assertThat(mContentIdStringCaptor.getValue()).isEqualTo(CONTENT_ID_STRING);
-
-        verify(mLocalActionMutation).commit();
-
-        verify(mFeedSessionManager).getUpdateConsumer(mMutationContextCaptor.capture());
-        assertThat(mMutationContextCaptor.getValue().getRequestingSessionId())
-                .isEqualTo(SESSION_ID);
-
-        verify(mModelConsumer).accept(mModelCaptor.capture());
-        Result<Model> result = mModelCaptor.getValue();
-        assertThat(result.isSuccessful()).isTrue();
-        List<StreamDataOperation> streamDataOperations = result.getValue().streamDataOperations;
-        assertThat(streamDataOperations).hasSize(1);
-        StreamDataOperation streamDataOperation = streamDataOperations.get(0);
-        assertThat(streamDataOperation).isEqualTo(dataOperation);
-    }
-
-    @Test
-    public void dismiss() throws Exception {
-        StreamDataOperation dataOperation = buildBasicDismissOperation();
-
-        mActionManager.dismiss(Collections.singletonList(dataOperation), null);
-
-        verify(mModelConsumer).accept(mModelCaptor.capture());
-        Result<Model> result = mModelCaptor.getValue();
-        assertThat(result.isSuccessful()).isTrue();
-        List<StreamDataOperation> streamDataOperations = result.getValue().streamDataOperations;
-        assertThat(streamDataOperations).hasSize(1);
-        StreamDataOperation streamDataOperation = streamDataOperations.get(0);
-        assertThat(streamDataOperation).isEqualTo(dataOperation);
-    }
-
-    @Test
-    public void dismiss_sessionIdSet() throws Exception {
-        StreamDataOperation dataOperation = buildBasicDismissOperation();
-
-        mActionManager.dismiss(Collections.singletonList(dataOperation), SESSION_ID);
-
-        verify(mFeedSessionManager).getUpdateConsumer(mMutationContextCaptor.capture());
-        assertThat(mMutationContextCaptor.getValue().getRequestingSessionId())
-                .isEqualTo(SESSION_ID);
-
-        verify(mModelConsumer).accept(mModelCaptor.capture());
-        Result<Model> result = mModelCaptor.getValue();
-        assertThat(result.isSuccessful()).isTrue();
-        List<StreamDataOperation> streamDataOperations = result.getValue().streamDataOperations;
-        assertThat(streamDataOperations).hasSize(1);
-        StreamDataOperation streamDataOperation = streamDataOperations.get(0);
-        assertThat(streamDataOperation).isEqualTo(dataOperation);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void triggerCreateAndUploadAction_whenUploadDisabled_byCantUploadWithNotice()
-            throws Exception {
-        // Set things so that, when the conditional upload feature is enabled, the upload of clicks
-        // and views cannot take place.
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-        when(mPrefService.getBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD)).thenReturn(true);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mActionManager.createAndUploadAction(
-                CONTENT_ID_STRING, ACTION_PAYLOAD, ActionManager.UploadActionType.CLICK);
-        verify(mFeedSessionManager, never()).triggerUploadActions(mActionCaptor.capture());
-
-        triggerViewActionAndVerifyUpserted(/* expectUpserted= */ false);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void triggerCreateAndUploadAction_whenUploadEnabled_byCanUploadWithNotice()
-            throws Exception {
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(true);
-        when(mPrefService.getBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD)).thenReturn(true);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mActionManager.createAndUploadAction(
-                CONTENT_ID_STRING, ACTION_PAYLOAD, ActionManager.UploadActionType.CLICK);
-        verify(mFeedSessionManager).triggerUploadActions(mActionCaptor.capture());
-        StreamUploadableAction action =
-                (StreamUploadableAction) mActionCaptor.getValue().toArray()[0];
-        assertThat(action.getFeatureContentId()).isEqualTo(CONTENT_ID_STRING);
-        assertThat(action.getTimestampSeconds()).isEqualTo(DEFAULT_TIME_SECONDS);
-        assertThat(action.getPayload()).isEqualTo(ACTION_PAYLOAD);
-
-        triggerViewActionAndVerifyUpserted(/* expectUpserted= */ true);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void triggerCreateAndUploadAction_whenUploadEnabled_byNoClickAction() throws Exception {
-        // Set things so that, when the conditional upload feature is enabled, the upload of clicks
-        // and views cannot take place.
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-        when(mPrefService.getBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD)).thenReturn(true);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mActionManager.createAndUploadAction(
-                CONTENT_ID_STRING, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-        verify(mFeedSessionManager).triggerUploadActions(mActionCaptor.capture());
-        StreamUploadableAction action =
-                (StreamUploadableAction) mActionCaptor.getValue().toArray()[0];
-        assertThat(action.getFeatureContentId()).isEqualTo(CONTENT_ID_STRING);
-        assertThat(action.getTimestampSeconds()).isEqualTo(DEFAULT_TIME_SECONDS);
-        assertThat(action.getPayload()).isEqualTo(ACTION_PAYLOAD);
-
-        // Try to store a view action and verify it isn't stored because, althought explicit actions
-        // can be uploaded, clicks and views cannot be yet uploaded.
-        triggerViewActionAndVerifyUpserted(/* expectUpserted= */ false);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void triggerCreateAndUploadAction_whenLogEnabled_byNoNoticeCard() throws Exception {
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-        when(mPrefService.getBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD)).thenReturn(false);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mActionManager.createAndUploadAction(
-                CONTENT_ID_STRING, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-        verify(mFeedSessionManager).triggerUploadActions(mActionCaptor.capture());
-        StreamUploadableAction action =
-                (StreamUploadableAction) mActionCaptor.getValue().toArray()[0];
-        assertThat(action.getFeatureContentId()).isEqualTo(CONTENT_ID_STRING);
-        assertThat(action.getTimestampSeconds()).isEqualTo(DEFAULT_TIME_SECONDS);
-        assertThat(action.getPayload()).isEqualTo(ACTION_PAYLOAD);
-
-        triggerViewActionAndVerifyUpserted(/* expectUpserted= */ true);
-    }
-
-    @Test
-    @Features.DisableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void triggerCreateAndUploadAction_whenLogEnabled_byCondUploadFeatureDisabled()
-            throws Exception {
-        // Set things so that, when the conditional upload feature is enabled, the upload of clicks
-        // and views would not take place.
-        mActionManager.setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-        when(mPrefService.getBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD)).thenReturn(true);
-
-        mFakeClock.set(DEFAULT_TIME);
-        mActionManager.createAndUploadAction(
-                CONTENT_ID_STRING, ACTION_PAYLOAD, ActionManager.UploadActionType.MISC);
-        verify(mFeedSessionManager).triggerUploadActions(mActionCaptor.capture());
-        StreamUploadableAction action =
-                (StreamUploadableAction) mActionCaptor.getValue().toArray()[0];
-        assertThat(action.getFeatureContentId()).isEqualTo(CONTENT_ID_STRING);
-        assertThat(action.getTimestampSeconds()).isEqualTo(DEFAULT_TIME_SECONDS);
-        assertThat(action.getPayload()).isEqualTo(ACTION_PAYLOAD);
-
-        triggerViewActionAndVerifyUpserted(/* expectUpserted= */ true);
-    }
-
-    @Test
-    public void triggerUploadAllActions() throws Exception {
-        String url = "url";
-        String param = "param";
-        ConsistencyToken token = ConsistencyToken.newBuilder()
-                                         .setToken(ByteString.copyFrom(new byte[] {0x1, 0x2}))
-                                         .build();
-        String expectedUrl = FeedActionManagerImpl.updateParam(url, param, token.toByteArray());
-        Consumer<String> consumer = result -> {
-            assertThat(result).isEqualTo(expectedUrl);
-        };
-        mActionManager.uploadAllActionsAndUpdateUrl(url, param, consumer);
-        verify(mFeedSessionManager).fetchActionsAndUpload(mConsumerCaptor.capture());
-        mConsumerCaptor.getValue().accept(Result.success(token));
-    }
-
-    @Test
-    public void onShow_viewTracked_coverage() {
-        showThenHide(
-                // View covers >50% (FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD) of viewport,
-                // but <50% (FeedActionManagerImpl.VIEW_EXPOSURE_THRESHOLD) of the view is visible.
-                new Rect(0, -1000, 100, 51), CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
-    }
-
-    @Test
-    public void onShow_viewTracked_exposure() {
-        showThenHide(
-                // >50% (FeedActionManagerImpl.VIEW_EXPOSURE_THRESHOLD) of the view is visible, but
-                // it covers <50% (FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD) of the
-                // viewport.
-                new Rect(0, -48, 100, 49), CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
-    }
-
-    @Test
-    public void onShow_viewTracked_noExposure_noCoverage() {
-        showThenHide(
-                // <50% (FeedActionManagerImpl.VIEW_EXPOSURE_THRESHOLD) of the view is visible and
-                // it covers <50% (FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD) of the
-                // viewport.
-                new Rect(0, -50, 100, 49), CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onShow_viewTracked_visible_tooShort() {
-        showThenHide(VISIBLE_RECT, CONTENT_ID_STRING, SHORT_DURATION_MS);
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onShow_viewTracked_visible_repeated_tooShort() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(SHORT_DURATION_MS);
-        mActionManager.onHide();
-
-        mFakeClock.advance(1000);
-
-        mActionManager.onShow();
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(SHORT_DURATION_MS);
-        mActionManager.onHide();
-
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onShow_viewTracked_visible_repeated() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-
-        mFakeClock.advance(1000);
-
-        mActionManager.onShow();
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-
-        verifyActionsUpserted(StreamUploadableAction.newBuilder()
-                                      .setFeatureContentId(CONTENT_ID_STRING)
-                                      .setPayload(ACTION_PAYLOAD)
-                                      .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
-                                      .setDurationMs(LONG_DURATION_MS)
-                                      .build(),
-                StreamUploadableAction.newBuilder()
-                        .setFeatureContentId(CONTENT_ID_STRING)
-                        .setPayload(ACTION_PAYLOAD)
-                        .setTimestampSeconds(
-                                DEFAULT_TIME_SECONDS + LONG_DURATION_S + 1 + LONG_DURATION_S)
-                        .setDurationMs(LONG_DURATION_MS)
-                        .build());
-    }
-
-    @Features.DisableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    @Test
-    public void onShow_viewTrackedVisible_featureDisabled() {
-        showThenHide(VISIBLE_RECT, CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onShow_viewNotTracked() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onShow_multipleViewsTracked() {
-        TestView view1 = new TestView();
-        TestView parentview = new TestView();
-        TestView view2 = new TestView();
-        TestView view3 = new TestView();
-        TestView view4 = new TestView();
-
-        mActionManager.onShow();
-
-        // Four views in the viewport, in vertical sequence, all visible except the last one.
-        view1.setRectOnScreen(new Rect(0, 0, 100, 20));
-        mViewport.children.add(view1);
-        view2.setRectOnScreen(new Rect(0, 30, 100, 50));
-        view3.setRectOnScreen(new Rect(0, 60, 100, 80));
-        view4.setRectOnScreen(new Rect(0, 90, 100, 1000));
-        parentview.children.add(view2);
-        parentview.children.add(view3);
-        parentview.children.add(view4);
-        mViewport.children.add(parentview);
-
-        // All views tracked except the second one.
-        mActionManager.onViewVisible(view1, "contentId1", ACTION_PAYLOAD);
-        mActionManager.onViewVisible(view3, "contentId3", ACTION_PAYLOAD);
-        mActionManager.onViewVisible(view4, "contentId4", ACTION_PAYLOAD);
-
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-
-        verifyActionsUpserted(StreamUploadableAction.newBuilder()
-                                      .setFeatureContentId("contentId1")
-                                      .setPayload(ACTION_PAYLOAD)
-                                      .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
-                                      .setDurationMs(LONG_DURATION_MS)
-                                      .build(),
-                StreamUploadableAction.newBuilder()
-                        .setFeatureContentId("contentId3")
-                        .setPayload(ACTION_PAYLOAD)
-                        .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
-                        .setDurationMs(LONG_DURATION_MS)
-                        .build());
-    }
-
-    private void showThenHide(Rect viewRect, String contentId, long preHideDurationMs) {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(viewRect);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, contentId, ACTION_PAYLOAD);
-        mFakeClock.advance(preHideDurationMs);
-        mActionManager.onHide();
-    }
-
-    @Test
-    public void onScroll_visible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mActionManager.onScrollStart();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onScrollEnd();
-        mFakeClock.advance(1000);
-        mActionManager.onHide();
-        verifyActionUpserted(
-                ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1, LONG_DURATION_MS);
-    }
-
-    @Test
-    public void onScroll_notVisible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mActionManager.onScrollStart();
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onScrollEnd();
-        mActionManager.onHide();
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onAnimationFinished_visible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        view.setRectOnScreen(VISIBLE_RECT);
-        mActionManager.onAnimationFinished();
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
-    }
-
-    @Test
-    public void onAnimationFinished_notVisible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mActionManager.onAnimationFinished();
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void onLayoutChange_visible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        view.setRectOnScreen(VISIBLE_RECT);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onLayoutChange();
-        mFakeClock.advance(1000);
-        mActionManager.onHide();
-        verifyActionUpserted(
-                ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1, LONG_DURATION_MS);
-    }
-
-    @Test
-    public void onLayoutChange_notVisible() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        view.setRectOnScreen(INVISIBLE_RECT);
-        mActionManager.onLayoutChange();
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-        verifyNoActionUpserted();
-    }
-
-    @Test
-    public void storeViewActions() {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onLayoutChange();
-        verifyNoActionUpserted();
-        mActionManager.storeViewActions(() -> {
-            verifyActionUpserted(
-                    ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
-            mStoreViewActionsRunnable.run();
-        });
-        verify(mStoreViewActionsRunnable).run();
-    }
-
-    @Test
-    public void storeViewActions_calledbackNoActions() {
-        mActionManager.storeViewActions(() -> {
-            verifyNoActionUpserted();
-            mStoreViewActionsRunnable.run();
-        });
-        verify(mStoreViewActionsRunnable, times(1)).run();
-    }
-
-    private void verifyActionUpserted(
-            ActionPayload payload, String contentId, long elapsedTimeS, long durationMs) {
-        verifyActionsUpserted(StreamUploadableAction.newBuilder()
-                                      .setFeatureContentId(contentId)
-                                      .setPayload(payload)
-                                      .setTimestampSeconds(DEFAULT_TIME_SECONDS + elapsedTimeS)
-                                      .setDurationMs(durationMs)
-                                      .build());
-    }
-
-    private void verifyActionsUpserted(StreamUploadableAction... actions) {
-        verify(mUploadableActionMutation, times(actions.length))
-                .upsert(mUploadableActionCaptor.capture(), mContentIdStringCaptor.capture());
-        assertThat(mUploadableActionCaptor.getAllValues())
-                .containsExactlyElementsIn(Arrays.asList(actions));
-        List<String> contentIds = new LinkedList<>();
-        for (StreamUploadableAction action : actions) {
-            contentIds.add(action.getFeatureContentId());
-        }
-        assertThat(mContentIdStringCaptor.getAllValues()).containsExactlyElementsIn(contentIds);
-    }
-
-    private void verifyNoActionUpserted() {
-        verify(mUploadableActionMutation, never())
-                .upsert(any(StreamUploadableAction.class), anyString());
-    }
-
-    private StreamDataOperation buildBasicDismissOperation() {
-        return StreamDataOperation.newBuilder()
-                .setStreamStructure(StreamStructure.newBuilder()
-                                            .setContentId(CONTENT_ID_STRING)
-                                            .setOperation(Operation.REMOVE))
-                .build();
-    }
-
-    private void setUpDismissMocks() {
-        when(mFeedSessionManager.getUpdateConsumer(any(MutationContext.class)))
-                .thenReturn(mModelConsumer);
-        when(mLocalActionMutation.add(anyInt(), anyString())).thenReturn(mLocalActionMutation);
-        when(mStore.editLocalActions()).thenReturn(mLocalActionMutation);
-    }
-
-    private void setupCreateAndStoreMocks() {
-        when(mUploadableActionMutation.upsert(any(StreamUploadableAction.class), anyString()))
-                .thenReturn(mUploadableActionMutation);
-        when(mStore.editUploadableActions()).thenReturn(mUploadableActionMutation);
-    }
-
-    private FakeTaskQueue getTaskQueue() {
-        FakeTaskQueue fakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        fakeTaskQueue.initialize(() -> {});
-        return fakeTaskQueue;
-    }
-
-    private class TestView extends View {
-        private Rect mRectOnScreen;
-        public final List<View> children = new LinkedList<>();
-
-        public TestView() {
-            super(Robolectric.buildActivity(Activity.class).get());
-            mRectOnScreen = new Rect(0, 0, 0, 0);
-        }
-
-        public void setRectOnScreen(Rect rect) {
-            mRectOnScreen = new Rect(rect);
-        }
-
-        public Rect getRectOnScreen() {
-            return new Rect(mRectOnScreen);
-        }
-    }
-
-    private class TestViewHandler extends FeedActionManagerImpl.ViewHandler {
-        @Override
-        public int getChildCount(View view) {
-            return ((TestView) view).children.size();
-        }
-
-        @Override
-        public View getChildAt(View view, int index) {
-            return ((TestView) view).children.get(index);
-        }
-
-        @Override
-        public Rect getRectOnScreen(View view) {
-            return ((TestView) view).getRectOnScreen();
-        }
-    }
-
-    private void triggerViewActionAndVerifyUpserted(boolean expectUpserted) {
-        mActionManager.onShow();
-        TestView view = new TestView();
-        view.setRectOnScreen(VISIBLE_RECT);
-        mViewport.children.add(view);
-        mActionManager.onViewVisible(view, CONTENT_ID_STRING, ACTION_PAYLOAD);
-        mFakeClock.advance(LONG_DURATION_MS);
-        mActionManager.onHide();
-
-        if (expectUpserted) {
-            verifyActionsUpserted(
-                    StreamUploadableAction.newBuilder()
-                            .setFeatureContentId(CONTENT_ID_STRING)
-                            .setPayload(ACTION_PAYLOAD)
-                            .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
-                            .setDurationMs(LONG_DURATION_MS)
-                            .build());
-        } else {
-            verify(mUploadableActionMutation, never())
-                    .upsert(mUploadableActionCaptor.capture(), mContentIdStringCaptor.capture());
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java
deleted file mode 100644
index 93f22d6..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java
+++ /dev/null
@@ -1,1072 +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.
-
-package org.chromium.chrome.browser.feed.library.feedactionparser;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentMetadata;
-import org.chromium.chrome.browser.feed.library.api.host.action.StreamActionApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ActionType;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipInfo;
-import org.chromium.chrome.browser.feed.library.api.internal.actionparser.ActionSource;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.feedactionparser.internal.PietFeedActionPayloadRetriever;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionPayloadProto.FeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.BlockContentData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.DismissData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.ElementType;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.FeedActionMetadata.Type;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.Insets;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.LabelledFeedActionData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.NotInterestedInData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.OpenContextMenuData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.OpenUrlData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.TooltipData;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.TooltipData.FeatureName;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.UndoAction;
-import org.chromium.components.feed.core.proto.ui.action.FeedActionProto.ViewReportData;
-import org.chromium.components.feed.core.proto.ui.action.PietExtensionsProto.PietFeedActionPayload;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.wire.ActionPayloadProto.ActionPayload;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests for {@link FeedActionParser}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedActionParserTest {
-    private static final String URL = "www.google.com";
-    private static final String PARAM = "param";
-
-    // clang-format off
-
-    private static final ContentId CONTENT_ID = ContentId.newBuilder().setId(123).build();
-
-    private static final String CONTENT_ID_STRING = "contentId";
-
-    private static final FeedActionPayload OPEN_URL_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder().setUrl(URL)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_WITH_PARAM_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setConsistencyTokenQueryParamName(
-                        PARAM)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_WITH_CLICK_PAYLOAD_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setContentId(CONTENT_ID)
-                    .setPayload(ActionPayload.getDefaultInstance())))
-            .build())
-        .build();
-
-    private static final FeedActionPayload CONTEXT_MENU_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_CONTEXT_MENU)
-                .setOpenContextMenuData(
-                    OpenContextMenuData.newBuilder().addContextMenuData(
-                        LabelledFeedActionData
-                        .newBuilder()
-                        .setLabel("Open url")
-                        .setFeedActionPayload(
-                            OPEN_URL_FEED_ACTION))))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_NEW_WINDOW_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_NEW_WINDOW)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder().setUrl(URL)))
-            .build())
-        .build();
-    private static final FeedActionPayload OPEN_URL_NEW_WINDOW_WITH_PARAM_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_NEW_WINDOW)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setConsistencyTokenQueryParamName(
-                        PARAM)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_INCOGNITO_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_INCOGNITO)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder().setUrl(URL)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_INCOGNITO_WITH_CLICK_PAYLOAD_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_INCOGNITO)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setContentId(CONTENT_ID)
-                    .setPayload(ActionPayload.getDefaultInstance())))
-            .build())
-        .build();
-
-    private static final FeedActionPayload OPEN_URL_INCOGNITO_WITH_PARAM_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_INCOGNITO)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setConsistencyTokenQueryParamName(
-                        PARAM)))
-            .build())
-        .build();
-
-
-
-    private static final FeedActionPayload OPEN_URL_NEW_TAB_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_NEW_TAB)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder().setUrl(URL)))
-            .build())
-        .build();
-    private static final FeedActionPayload OPEN_URL_NEW_TAB_WITH_PARAM_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL_NEW_TAB)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder()
-                    .setUrl(URL)
-                    .setConsistencyTokenQueryParamName(
-                        PARAM)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload DOWNLOAD_URL_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(FeedActionMetadata.newBuilder()
-              .setType(Type.DOWNLOAD)
-              .build())
-            .build())
-        .build();
-
-    private static final FeedActionPayload LEARN_MORE_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(FeedActionMetadata.newBuilder().setType(
-                Type.LEARN_MORE))
-            .build())
-        .build();
-
-    private static final FeedActionPayload MANAGE_INTERESTS_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.OPEN_URL)
-                .setOpenUrlData(
-                    OpenUrlData.newBuilder().setUrl(
-                        FeedActionParser.EXPECTED_MANAGE_INTERESTS_URL)))
-                .build())
-            .build();
-
-    private static final FeedActionPayload SEND_FEEDBACK_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(FeedActionMetadata.newBuilder().setType(
-                Type.SEND_FEEDBACK))
-            .build())
-        .build();
-
-    private static final Action OPEN_URL_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action OPEN_URL_WITH_PARAM_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_WITH_PARAM_FEED_ACTION)
-            .build())
-        .build();
-
-       private static final Action OPEN_URL_WITH_CLICK_PAYLOAD_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_WITH_CLICK_PAYLOAD_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action OPEN_INCOGNITO_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_INCOGNITO_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action OPEN_INCOGNITO_WITH_CLICK_PAYLOAD_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_INCOGNITO_WITH_CLICK_PAYLOAD_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action OPEN_INCOGNITO_WITH_PARAM_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_INCOGNITO_WITH_PARAM_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action OPEN_NEW_WINDOW_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_NEW_WINDOW_FEED_ACTION)
-            .build())
-        .build();
-    private static final Action OPEN_NEW_WINDOW_WITH_PARAM_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(
-                OPEN_URL_NEW_WINDOW_WITH_PARAM_FEED_ACTION)
-            .build())
-        .build();
-    private static final Action OPEN_NEW_TAB_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_NEW_TAB_FEED_ACTION)
-            .build())
-        .build();
-    private static final Action OPEN_NEW_TAB_WITH_PARAM_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(OPEN_URL_NEW_TAB_WITH_PARAM_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action LEARN_MORE_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(LEARN_MORE_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action MANAGE_INTERESTS_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(MANAGE_INTERESTS_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final Action SEND_FEEDBACK_ACTION =
-        Action.newBuilder()
-        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-            PietFeedActionPayload.newBuilder()
-            .setFeedActionPayload(SEND_FEEDBACK_FEED_ACTION)
-            .build())
-        .build();
-
-    private static final UndoAction UNDO_ACTION =
-        UndoAction.newBuilder().setConfirmationLabel("confirmation").build();
-
-    private static final FeedActionPayload DISMISS_LOCAL_FEED_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.DISMISS_LOCAL)
-                .setDismissData(
-                    DismissData.newBuilder()
-                    .addDataOperations(
-                        DataOperation
-                        .getDefaultInstance())
-                    .setContentId(
-                        CONTENT_ID)
-                    .setUndoAction(UNDO_ACTION)))
-            .build())
-        .build();
-
-     private static final FeedActionPayload BLOCK_CONTENT_ACTION =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.BLOCK_CONTENT)
-                .setBlockContentData(
-                    BlockContentData.newBuilder()
-                    .addDataOperations(
-                        DataOperation
-                        .getDefaultInstance())
-                    .setPayload(ActionPayload.getDefaultInstance())
-                    ))
-            .build())
-        .build();
-
-    private static final FeedActionPayload NOT_INTERESTED_IN =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.NOT_INTERESTED_IN)
-                .setNotInterestedInData(
-                    NotInterestedInData.newBuilder()
-                    .addDataOperations(
-                        DataOperation
-                        .getDefaultInstance())
-                    .setInterestTypeValue(
-                        NotInterestedInData
-                        .RecordedInterestType
-                        .SOURCE
-                        .getNumber())
-                    .setPayload(
-                        ActionPayload
-                        .getDefaultInstance())
-                    .setUndoAction(UNDO_ACTION)))
-            .build())
-        .build();
-
-    private static final FeedActionPayload VIEW_ELEMENT =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.VIEW_ELEMENT)
-                .setElementTypeValue(ElementType.INTEREST_HEADER
-                  .getNumber()))
-            .build())
-        .build();
-
-    private static final FeedActionPayload HIDE_ELEMENT =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.HIDE_ELEMENT)
-                .setElementTypeValue(ElementType.INTEREST_HEADER
-                  .getNumber()))
-            .build())
-        .build();
-
-    private static final FeedActionPayload CLICK_ELEMENT =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.HIDE_ELEMENT)
-                .setElementTypeValue(ElementType.INTEREST_HEADER
-                  .getNumber()))
-            .build())
-        .build();
-
-    private static final FeedActionPayload DISMISS_LOCAL_FEED_ACTION_NO_CONTENT_ID =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(
-                FeedActionMetadata.newBuilder()
-                .setType(Type.DISMISS_LOCAL)
-                .setDismissData(
-                    DismissData.newBuilder().addDataOperations(
-                        DataOperation
-                        .getDefaultInstance())))
-            .build())
-        .build();
-
-    private static final ContentMetadata CONTENT_METADATA = new ContentMetadata(URL, "title",
-        /* timePublished= */ -1,
-        /* imageUrl= */ null,
-        /* publisher= */ null,
-        /* faviconUrl= */ null,
-        /* snippet= */ null);
-
-    private static final FeedActionPayload OPEN_URL_MISSING_URL =
-        FeedActionPayload.newBuilder()
-        .setExtension(FeedAction.feedActionExtension,
-            FeedAction.newBuilder()
-            .setMetadata(FeedActionMetadata.newBuilder()
-              .setType(Type.OPEN_URL)
-              .setOpenUrlData(
-                  OpenUrlData.getDefaultInstance()))
-            .build())
-        .build();
-
-    @Mock
-    private StreamActionApi mStreamActionApi;
-    @Mock
-    private ProtocolAdapter mProtocolAdapter;
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-
-    List<StreamDataOperation> mStreamDataOperations = Lists.newArrayList(
-            StreamDataOperation.newBuilder()
-                    .setStreamStructure(
-                            StreamStructure.newBuilder().setContentId("dataOpContentId"))
-                    .build());
-
-    private FeedActionParser mFeedActionParser;
-
-    // clang-format on
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        when(mProtocolAdapter.getStreamContentId(CONTENT_ID)).thenReturn(CONTENT_ID_STRING);
-        mFeedActionParser = new FeedActionParser(mProtocolAdapter,
-                new PietFeedActionPayloadRetriever(), () -> CONTENT_METADATA, mBasicLoggingApi);
-    }
-
-    @Test
-    public void testParseAction() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_URL_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrl(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL);
-    }
-
-    @Test
-    public void testParseAction_manageInterests() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-        mFeedActionParser.parseAction(MANAGE_INTERESTS_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrl(FeedActionParser.EXPECTED_MANAGE_INTERESTS_URL);
-        verify(mStreamActionApi).onClientAction(ActionType.MANAGE_INTERESTS);
-    }
-
-    @Test
-    public void testParseAction_openUrlWithParam() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_URL_WITH_PARAM_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrl(URL, PARAM);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL);
-    }
-
-    @Test
-    public void testParseAction_newWindow() {
-        when(mStreamActionApi.canOpenUrlInNewWindow()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_NEW_WINDOW_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInNewWindow(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_NEW_WINDOW);
-    }
-
-    @Test
-    public void testParseAction_newWindowWithParam() {
-        when(mStreamActionApi.canOpenUrlInNewWindow()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_NEW_WINDOW_WITH_PARAM_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInNewWindow(URL, PARAM);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_NEW_WINDOW);
-    }
-
-    @Test
-    public void testParseAction_incognito() {
-        when(mStreamActionApi.canOpenUrlInIncognitoMode()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_INCOGNITO_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInIncognitoMode(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_INCOGNITO);
-    }
-
-    @Test
-    public void testParseAction_incognitoWithParam() {
-        when(mStreamActionApi.canOpenUrlInIncognitoMode()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_INCOGNITO_WITH_PARAM_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInIncognitoMode(URL, PARAM);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_INCOGNITO);
-    }
-
-    @Test
-    public void testParseAction_incognitoNoClickAction() {
-        when(mStreamActionApi.canOpenUrlInIncognitoMode()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_INCOGNITO_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi, never()).reportClickAction(anyString(), any(ActionPayload.class));
-
-        verify(mStreamActionApi).openUrlInIncognitoMode(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_INCOGNITO);
-    }
-
-    @Test
-    public void testParseAction_newTab() {
-        when(mStreamActionApi.canOpenUrlInNewTab()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_NEW_TAB_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInNewTab(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_NEW_TAB);
-    }
-
-    @Test
-    public void testParseAction_newTabWithParam() {
-        when(mStreamActionApi.canOpenUrlInNewTab()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_NEW_TAB_WITH_PARAM_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi).openUrlInNewTab(URL, PARAM);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL_NEW_TAB);
-    }
-
-    @Test
-    public void testParseAction_withClickAction() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-        mFeedActionParser.parseAction(OPEN_URL_WITH_CLICK_PAYLOAD_ACTION, mStreamActionApi,
-                /* view= */ null, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi)
-                .reportClickAction(CONTENT_ID_STRING, ActionPayload.getDefaultInstance());
-
-        verify(mStreamActionApi).openUrl(URL);
-        verify(mStreamActionApi).onClientAction(ActionType.OPEN_URL);
-    }
-
-    @Test
-    public void testParseAction_contextMenu() {
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        View view = new View(context);
-
-        when(mStreamActionApi.canOpenContextMenu()).thenReturn(true);
-        PietFeedActionPayload contextMenuPietFeedAction =
-                PietFeedActionPayload.newBuilder()
-                        .setFeedActionPayload(CONTEXT_MENU_FEED_ACTION)
-                        .build();
-
-        mFeedActionParser.parseAction(
-                Action.newBuilder()
-                        .setExtension(PietFeedActionPayload.pietFeedActionPayloadExtension,
-                                contextMenuPietFeedAction)
-                        .build(),
-                mStreamActionApi,
-                /* view= */ view, LogData.getDefaultInstance(), ActionSource.CLICK);
-
-        verify(mStreamActionApi)
-                .openContextMenu(contextMenuPietFeedAction.getFeedActionPayload()
-                                         .getExtension(FeedAction.feedActionExtension)
-                                         .getMetadata()
-                                         .getOpenContextMenuData(),
-                        view);
-    }
-
-    @Test
-    public void testParseAction_downloadUrl() {
-        when(mStreamActionApi.canDownloadUrl()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                DOWNLOAD_URL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi).downloadUrl(CONTENT_METADATA);
-        verify(mStreamActionApi).onClientAction(ActionType.DOWNLOAD);
-    }
-
-    @Test
-    public void testParseAction_reportViewVisible() {
-        FeedAction reportViewAction =
-                FeedAction.newBuilder()
-                        .setMetadata(
-                                FeedActionMetadata.newBuilder()
-                                        .setType(Type.REPORT_VIEW)
-                                        .setViewReportData(
-                                                ViewReportData.newBuilder()
-                                                        .setContentId(CONTENT_ID)
-                                                        .setVisibility(
-                                                                ViewReportData.Visibility.SHOW)
-                                                        .setPayload(ActionPayload
-                                                                            .getDefaultInstance())))
-                        .build();
-
-        FeedActionPayload reportViewActionPayload =
-                FeedActionPayload.newBuilder()
-                        .setExtension(FeedAction.feedActionExtension, reportViewAction)
-                        .build();
-
-        View view = new View(Robolectric.buildActivity(Activity.class).get());
-
-        mFeedActionParser.parseFeedActionPayload(
-                reportViewActionPayload, mStreamActionApi, view, ActionSource.VIEW);
-        verify(mStreamActionApi)
-                .reportViewVisible(view, CONTENT_ID_STRING, ActionPayload.getDefaultInstance());
-    }
-
-    @Test
-    public void testParseAction_reportViewHidden() {
-        FeedAction reportViewAction =
-                FeedAction.newBuilder()
-                        .setMetadata(
-                                FeedActionMetadata.newBuilder()
-                                        .setType(Type.REPORT_VIEW)
-                                        .setViewReportData(
-                                                ViewReportData.newBuilder()
-                                                        .setContentId(CONTENT_ID)
-                                                        .setVisibility(
-                                                                ViewReportData.Visibility.HIDE)
-                                                        .setPayload(ActionPayload
-                                                                            .getDefaultInstance())))
-                        .build();
-
-        FeedActionPayload reportViewActionPayload =
-                FeedActionPayload.newBuilder()
-                        .setExtension(FeedAction.feedActionExtension, reportViewAction)
-                        .build();
-
-        View view = new View(Robolectric.buildActivity(Activity.class).get());
-
-        mFeedActionParser.parseFeedActionPayload(
-                reportViewActionPayload, mStreamActionApi, view, ActionSource.VIEW);
-        verify(mStreamActionApi).reportViewHidden(view, CONTENT_ID_STRING);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_openUrl() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-        assertThat(mFeedActionParser.canPerformAction(OPEN_URL_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canOpenUrl();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_newWindow() {
-        when(mStreamActionApi.canOpenUrlInNewWindow()).thenReturn(true);
-        assertThat(mFeedActionParser.canPerformAction(
-                           OPEN_URL_NEW_WINDOW_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canOpenUrlInNewWindow();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_incognito() {
-        when(mStreamActionApi.canOpenUrlInIncognitoMode()).thenReturn(true);
-        assertThat(mFeedActionParser.canPerformAction(
-                           OPEN_URL_INCOGNITO_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canOpenUrlInIncognitoMode();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_nestedContextMenu() {
-        when(mStreamActionApi.canOpenContextMenu()).thenReturn(true);
-
-        assertThat(mFeedActionParser.canPerformAction(CONTEXT_MENU_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canOpenContextMenu();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_downloadUrl() {
-        when(mStreamActionApi.canDownloadUrl()).thenReturn(true);
-        assertThat(mFeedActionParser.canPerformAction(DOWNLOAD_URL_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canDownloadUrl();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testCanPerformActionFromContextMenu_learnMore() {
-        when(mStreamActionApi.canLearnMore()).thenReturn(true);
-        assertThat(mFeedActionParser.canPerformAction(LEARN_MORE_FEED_ACTION, mStreamActionApi))
-                .isTrue();
-        verify(mStreamActionApi).canLearnMore();
-        verifyNoMoreInteractions(mStreamActionApi);
-    }
-
-    @Test
-    public void testDownloadUrl_noContentMetadata() {
-        mFeedActionParser =
-                new FeedActionParser(mProtocolAdapter, new PietFeedActionPayloadRetriever(),
-                        /* contentMetadata= */ () -> null, mBasicLoggingApi);
-        when(mStreamActionApi.canDownloadUrl()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                DOWNLOAD_URL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi, times(0)).downloadUrl(any(ContentMetadata.class));
-    }
-
-    @Test
-    public void testDownloadUrl_noHostSupport() {
-        when(mStreamActionApi.canDownloadUrl()).thenReturn(false);
-        mFeedActionParser.parseFeedActionPayload(
-                DOWNLOAD_URL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi, times(0)).downloadUrl(any(ContentMetadata.class));
-    }
-
-    @Test
-    public void testDismiss_noApiSupport() {
-        when(mStreamActionApi.canDismiss()).thenReturn(false);
-        when(mProtocolAdapter.createOperations(
-                     DISMISS_LOCAL_FEED_ACTION.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getDismissData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-        mFeedActionParser.parseFeedActionPayload(
-                DISMISS_LOCAL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi, never())
-                .dismiss(anyString(), ArgumentMatchers.anyList(), any(UndoAction.class),
-                        any(ActionPayload.class));
-    }
-
-    @Test
-    public void testBlockContent() {
-        when(mProtocolAdapter.createOperations(
-                     BLOCK_CONTENT_ACTION.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getBlockContentData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-        mFeedActionParser.parseFeedActionPayload(
-                BLOCK_CONTENT_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi)
-                .handleBlockContent(mStreamDataOperations, ActionPayload.getDefaultInstance());
-        verify(mStreamActionApi).onClientAction(ActionType.BLOCK_CONTENT);
-    }
-
-    @Test
-    public void testNotInterestedIn_noApiSupport() {
-        when(mStreamActionApi.canHandleNotInterestedIn()).thenReturn(false);
-        when(mProtocolAdapter.createOperations(
-                     NOT_INTERESTED_IN.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getNotInterestedInData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-        mFeedActionParser.parseFeedActionPayload(
-                DISMISS_LOCAL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi, never())
-                .handleNotInterestedIn(ArgumentMatchers.anyList(), any(UndoAction.class),
-                        any(ActionPayload.class), anyInt());
-    }
-
-    @Test
-    public void testDismiss_noContentId() {
-        when(mStreamActionApi.canDismiss()).thenReturn(true);
-        when(mProtocolAdapter.createOperations(
-                     DISMISS_LOCAL_FEED_ACTION.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getDismissData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-        mFeedActionParser.parseFeedActionPayload(DISMISS_LOCAL_FEED_ACTION_NO_CONTENT_ID,
-                mStreamActionApi,
-                /* view= */ null, ActionSource.CLICK);
-        verify(mStreamActionApi, never())
-                .dismiss(anyString(), ArgumentMatchers.anyList(), any(UndoAction.class),
-                        any(ActionPayload.class));
-    }
-
-    @Test
-    public void testDismiss() {
-        when(mStreamActionApi.canDismiss()).thenReturn(true);
-
-        when(mProtocolAdapter.createOperations(
-                     DISMISS_LOCAL_FEED_ACTION.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getDismissData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-
-        mFeedActionParser.parseFeedActionPayload(
-                DISMISS_LOCAL_FEED_ACTION, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-
-        verify(mStreamActionApi)
-                .dismiss(CONTENT_ID_STRING, mStreamDataOperations, UNDO_ACTION,
-                        ActionPayload.getDefaultInstance());
-    }
-
-    @Test
-    public void testNotInterestedIn() {
-        when(mStreamActionApi.canDismiss()).thenReturn(true);
-        when(mStreamActionApi.canHandleNotInterestedIn()).thenReturn(true);
-
-        when(mProtocolAdapter.createOperations(
-                     NOT_INTERESTED_IN.getExtension(FeedAction.feedActionExtension)
-                             .getMetadata()
-                             .getNotInterestedInData()
-                             .getDataOperationsList()))
-                .thenReturn(mStreamDataOperations);
-
-        mFeedActionParser.parseFeedActionPayload(
-                NOT_INTERESTED_IN, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-
-        verify(mStreamActionApi)
-                .handleNotInterestedIn(mStreamDataOperations, UNDO_ACTION,
-                        ActionPayload.getDefaultInstance(),
-                        NotInterestedInData.RecordedInterestType.SOURCE.getNumber());
-    }
-
-    @Test
-    public void testLearnMore() {
-        when(mStreamActionApi.canLearnMore()).thenReturn(true);
-        mFeedActionParser.parseAction(LEARN_MORE_ACTION, mStreamActionApi,
-                /* view= */ null, /* veLoggingToken- */
-                null, ActionSource.CLICK);
-
-        verify(mStreamActionApi).learnMore();
-        verify(mStreamActionApi).onClientAction(ActionType.LEARN_MORE);
-    }
-
-    @Test
-    public void testLearnMore_noApiSupport() {
-        when(mStreamActionApi.canLearnMore()).thenReturn(false);
-        mFeedActionParser.parseAction(LEARN_MORE_ACTION, mStreamActionApi,
-                /* view= */ null, /* veLoggingToken- */
-                null, ActionSource.CLICK);
-
-        verify(mStreamActionApi, never()).learnMore();
-    }
-
-    @Test
-    public void testSendFeedback() {
-        when(mStreamActionApi.canLearnMore()).thenReturn(true);
-        mFeedActionParser.parseAction(SEND_FEEDBACK_ACTION, mStreamActionApi,
-                /* view= */ null, /* veLoggingToken- */
-                null, ActionSource.CLICK);
-
-        // TODO(petewil): Figure out how to verify that it worked as expected.
-    }
-
-    @Test
-    public void testOnHide() {
-        when(mStreamActionApi.canHandleElementHide()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                HIDE_ELEMENT, mStreamActionApi, /* view= */ null, ActionSource.VIEW);
-
-        verify(mStreamActionApi).onElementHide(ElementType.INTEREST_HEADER.getNumber());
-
-        verify(mStreamActionApi, never()).onElementClick(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOnView() {
-        when(mStreamActionApi.canHandleElementView()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                VIEW_ELEMENT, mStreamActionApi, /* view= */ null, ActionSource.VIEW);
-
-        verify(mStreamActionApi).onElementView(ElementType.INTEREST_HEADER.getNumber());
-
-        verify(mStreamActionApi, never()).onElementClick(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOnClick() {
-        when(mStreamActionApi.canHandleElementClick()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                CLICK_ELEMENT, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-
-        verify(mStreamActionApi).onElementClick(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOnClick_notLogClick() {
-        when(mStreamActionApi.canHandleElementClick()).thenReturn(true);
-        mFeedActionParser.parseFeedActionPayload(
-                VIEW_ELEMENT, mStreamActionApi, /* view= */ null, ActionSource.VIEW);
-
-        verify(mStreamActionApi, never()).onElementClick(ElementType.INTEREST_HEADER.getNumber());
-    }
-
-    @Test
-    public void testOpenUrl_noUrl_logsError() {
-        when(mStreamActionApi.canOpenUrl()).thenReturn(true);
-
-        mFeedActionParser.parseFeedActionPayload(
-                OPEN_URL_MISSING_URL, mStreamActionApi, /* view= */ null, ActionSource.CLICK);
-
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.NO_URL_FOR_OPEN);
-    }
-
-    @Test
-    public void testShowTooltip() {
-        String tooltipLabel = "tooltip";
-        String accessibilityLabel = "tool";
-        int topInsert = 1;
-        int bottomInsert = 3;
-        when(mStreamActionApi.canShowTooltip()).thenReturn(true);
-        ArgumentCaptor<TooltipInfo> tooltipInfoArgumentCaptor =
-                ArgumentCaptor.forClass(TooltipInfo.class);
-        FeedActionPayload tooltipData =
-                FeedActionPayload.newBuilder()
-                        .setExtension(FeedAction.feedActionExtension,
-                                FeedAction.newBuilder()
-                                        .setMetadata(
-                                                FeedActionMetadata.newBuilder()
-                                                        .setType(Type.SHOW_TOOLTIP)
-                                                        .setTooltipData(
-                                                                // clang-format off
-                                          TooltipData.newBuilder()
-                                                  .setLabel(tooltipLabel)
-                                                  .setAccessibilityLabel(
-                                                          accessibilityLabel)
-                                                  .setFeatureName(
-                                                          FeatureName
-                                                                  .CARD_MENU)
-                                                  .setInsets(
-                                                          Insets.newBuilder()
-                                                                  .setTop(topInsert)
-                                                                  .setBottom(
-                                                                          bottomInsert)
-                                                                  .build())
-                                                                // clang-format on
-                                                                ))
-                                        .build())
-                        .build();
-        View view = new View(Robolectric.buildActivity(Activity.class).get());
-        mFeedActionParser.parseFeedActionPayload(
-                tooltipData, mStreamActionApi, view, ActionSource.VIEW);
-        verify(mStreamActionApi).maybeShowTooltip(tooltipInfoArgumentCaptor.capture(), eq(view));
-        TooltipInfo tooltipInfo = tooltipInfoArgumentCaptor.getValue();
-        assertThat(tooltipInfo.getLabel()).isEqualTo(tooltipLabel);
-        assertThat(tooltipInfo.getAccessibilityLabel()).isEqualTo(accessibilityLabel);
-        assertThat(tooltipInfo.getFeatureName())
-                .isEqualTo(TooltipInfo.FeatureName.CARD_MENU_TOOLTIP);
-        assertThat(tooltipInfo.getTopInset()).isEqualTo(topInsert);
-        assertThat(tooltipInfo.getBottomInset()).isEqualTo(bottomInsert);
-    }
-
-    @Test
-    public void testShowTooltip_notSupported() {
-        when(mStreamActionApi.canShowTooltip()).thenReturn(false);
-
-        FeedActionPayload tooltipData =
-                FeedActionPayload.newBuilder()
-                        .setExtension(FeedAction.feedActionExtension,
-                                FeedAction.newBuilder()
-                                        .setMetadata(FeedActionMetadata.newBuilder().setType(
-                                                Type.SHOW_TOOLTIP))
-                                        .build())
-                        .build();
-        View view = new View(Robolectric.buildActivity(Activity.class).get());
-        mFeedActionParser.parseFeedActionPayload(
-                tooltipData, mStreamActionApi, view, ActionSource.VIEW);
-        verify(mStreamActionApi, never()).maybeShowTooltip(any(TooltipInfo.class), any(View.class));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionreader/FeedActionReaderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionreader/FeedActionReaderTest.java
deleted file mode 100644
index 809e4558..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionreader/FeedActionReaderTest.java
+++ /dev/null
@@ -1,293 +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.
-package org.chromium.chrome.browser.feed.library.feedactionreader;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.ArgumentMatchers.anyListOf;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionReader;
-import org.chromium.chrome.browser.feed.library.api.internal.common.DismissActionWithSemanticProperties;
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLocalAction;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Tests of the {@link FeedActionReader} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedActionReaderTest {
-    private static final ContentIdGenerators ID_GENERATOR = new ContentIdGenerators();
-
-    private static final ContentId CONTENT_ID = ResponseBuilder.createFeatureContentId(1);
-    private static final String CONTENT_ID_STRING = ID_GENERATOR.createContentId(CONTENT_ID);
-    private static final ContentId CONTENT_ID_2 = ResponseBuilder.createFeatureContentId(2);
-    private static final String CONTENT_ID_STRING_2 = ID_GENERATOR.createContentId(CONTENT_ID_2);
-    private static final long DEFAULT_TIME = TimeUnit.DAYS.toSeconds(42);
-
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-
-    @Mock
-    private Store mStore;
-    @Mock
-    private ProtocolAdapter mProtocolAdapter;
-    @Mock
-    private Configuration mConfiguration;
-
-    private ActionReader mActionReader;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-
-        when(mConfiguration.getValueOrDefault(
-                     same(ConfigKey.DEFAULT_ACTION_TTL_SECONDS), anyLong()))
-                .thenReturn(TimeUnit.DAYS.toSeconds(3));
-        when(mConfiguration.getValueOrDefault(
-                     same(ConfigKey.MINIMUM_VALID_ACTION_RATIO), anyDouble()))
-                .thenReturn(0.9);
-
-        when(mStore.triggerLocalActionGc(
-                     anyListOf(StreamLocalAction.class), anyListOf(String.class)))
-                .thenReturn(() -> {});
-
-        mActionReader = new FeedActionReader(
-                mStore, mFakeClock, mProtocolAdapter, getTaskQueue(), mConfiguration);
-
-        when(mProtocolAdapter.getWireContentId(CONTENT_ID_STRING))
-                .thenReturn(Result.success(CONTENT_ID));
-        when(mProtocolAdapter.getWireContentId(CONTENT_ID_STRING_2))
-                .thenReturn(Result.success(CONTENT_ID_2));
-    }
-
-    @Test
-    public void getAllDismissedActions() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        mockStoreCalls(Collections.singletonList(dismissAction), Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-        assertThat(dismissActions)
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID, null));
-    }
-
-    @Test
-    public void getAllDismissedActions_empty() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        mockStoreCalls(Collections.emptyList(), Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-        assertThat(dismissActions).hasSize(0);
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_storeError_getAllDismissActions() {
-        mFakeClock.set(DEFAULT_TIME);
-        when(mStore.getAllDismissLocalActions()).thenReturn(Result.failure());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isFalse();
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_storeError_getSemanticProperties() {
-        mFakeClock.set(DEFAULT_TIME);
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        when(mStore.getAllDismissLocalActions())
-                .thenReturn(Result.success(Collections.singletonList(dismissAction)));
-        when(mStore.getSemanticProperties(anyList())).thenReturn(Result.failure());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isFalse();
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_expired() {
-        mFakeClock.set(TimeUnit.SECONDS.toMillis(DEFAULT_TIME) + TimeUnit.DAYS.toMillis(3));
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        List<StreamLocalAction> dismissActions = Collections.singletonList(dismissAction);
-        mockStoreCalls(dismissActions, Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        assertThat(dismissActionsResult.getValue()).hasSize(0);
-        verify(mStore).triggerLocalActionGc(eq(dismissActions), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_semanticProperties() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        byte[] semanticData = {12, 41};
-        mockStoreCalls(Collections.singletonList(dismissAction),
-                Collections.singletonList(
-                        new SemanticPropertiesWithId(CONTENT_ID_STRING, semanticData)));
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-        assertThat(dismissActions)
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID, semanticData));
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_multipleActions() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        StreamLocalAction dismissAction2 = buildDismissAction(CONTENT_ID_STRING_2);
-        mockStoreCalls(Arrays.asList(dismissAction, dismissAction2), Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-
-        assertThat(dismissActions)
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID, null),
-                        new DismissActionWithSemanticProperties(CONTENT_ID_2, null));
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_multipleActions_semanticProperties() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        StreamLocalAction dismissAction2 = buildDismissAction(CONTENT_ID_STRING_2);
-        byte[] semanticData = {12, 41};
-        byte[] semanticData2 = {42, 72};
-        mockStoreCalls(Arrays.asList(dismissAction, dismissAction2),
-                Arrays.asList(new SemanticPropertiesWithId(CONTENT_ID_STRING, semanticData),
-                        new SemanticPropertiesWithId(CONTENT_ID_STRING_2, semanticData2)));
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-
-        assertThat(dismissActions)
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID, semanticData),
-                        new DismissActionWithSemanticProperties(CONTENT_ID_2, semanticData2));
-        verify(mStore, never())
-                .triggerLocalActionGc(anyListOf(StreamLocalAction.class), anyListOf(String.class));
-    }
-
-    @Test
-    public void getAllDismissedActions_multipleActions_someExpired() {
-        mFakeClock.set(TimeUnit.SECONDS.toMillis(DEFAULT_TIME) + TimeUnit.DAYS.toMillis(3));
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        StreamLocalAction dismissAction2 =
-                StreamLocalAction.newBuilder()
-                        .setAction(ActionType.DISMISS)
-                        .setFeatureContentId(CONTENT_ID_STRING_2)
-                        .setTimestampSeconds(DEFAULT_TIME + TimeUnit.DAYS.toSeconds(2))
-                        .build();
-        mockStoreCalls(Arrays.asList(dismissAction, dismissAction2), Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-
-        assertThat(dismissActionsResult.getValue())
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID_2, null));
-        verify(mStore).triggerLocalActionGc(Arrays.asList(dismissAction, dismissAction2),
-                Collections.singletonList(CONTENT_ID_STRING_2));
-    }
-
-    @Test
-    public void getAllDismissedActions_duplicateActions() {
-        mFakeClock.set(DEFAULT_TIME);
-
-        StreamLocalAction dismissAction = buildDismissAction(CONTENT_ID_STRING);
-        StreamLocalAction dismissAction2 = buildDismissAction(CONTENT_ID_STRING);
-        mockStoreCalls(Arrays.asList(dismissAction, dismissAction2), Collections.emptyList());
-
-        Result<List<DismissActionWithSemanticProperties>> dismissActionsResult =
-                mActionReader.getDismissActionsWithSemanticProperties();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        List<DismissActionWithSemanticProperties> dismissActions = dismissActionsResult.getValue();
-
-        assertThat(dismissActions).hasSize(1);
-        assertThat(dismissActions)
-                .containsExactly(new DismissActionWithSemanticProperties(CONTENT_ID, null));
-    }
-
-    private StreamLocalAction buildDismissAction(String contentId) {
-        return StreamLocalAction.newBuilder()
-                .setAction(ActionType.DISMISS)
-                .setFeatureContentId(contentId)
-                .setTimestampSeconds(DEFAULT_TIME)
-                .build();
-    }
-
-    private void mockStoreCalls(List<StreamLocalAction> dismissActions,
-            List<SemanticPropertiesWithId> semanticProperties) {
-        when(mStore.getAllDismissLocalActions()).thenReturn(Result.success(dismissActions));
-        when(mStore.getSemanticProperties(anyList()))
-                .thenReturn(Result.success(semanticProperties));
-    }
-
-    private FakeTaskQueue getTaskQueue() {
-        FakeTaskQueue fakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        fakeTaskQueue.initialize(() -> {});
-        return fakeTaskQueue;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedapplifecyclelistener/FeedAppLifecycleListenerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedapplifecyclelistener/FeedAppLifecycleListenerTest.java
deleted file mode 100644
index 9cc8ba3..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedapplifecyclelistener/FeedAppLifecycleListenerTest.java
+++ /dev/null
@@ -1,78 +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.
-
-package org.chromium.chrome.browser.feed.library.feedapplifecyclelistener;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedLifecycleListener.LifecycleEvent;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test class for {@link FeedAppLifecycleListener} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedAppLifecycleListenerTest {
-    @Mock
-    private FeedLifecycleListener mLifecycleListener;
-    @Mock
-    private ThreadUtils mThreadUtils;
-    private FeedAppLifecycleListener mAppLifecycleListener;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mAppLifecycleListener = new FeedAppLifecycleListener(mThreadUtils);
-        mAppLifecycleListener.registerObserver(mLifecycleListener);
-    }
-
-    @Test
-    public void onEnterForeground() {
-        mAppLifecycleListener.onEnterForeground();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.ENTER_FOREGROUND);
-    }
-
-    @Test
-    public void onEnterBackground() {
-        mAppLifecycleListener.onEnterBackground();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.ENTER_BACKGROUND);
-    }
-
-    @Test
-    public void onClearAll() {
-        mAppLifecycleListener.onClearAll();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.CLEAR_ALL);
-    }
-
-    @Test
-    public void onClearAllWithRefresh() {
-        mAppLifecycleListener.onClearAllWithRefresh();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.CLEAR_ALL_WITH_REFRESH);
-    }
-
-    @Test
-    public void onSignedIn() {
-        mAppLifecycleListener.onSignedIn();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.SIGNED_IN);
-    }
-
-    @Test
-    public void onSignedOut() {
-        mAppLifecycleListener.onSignedOut();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.SIGNED_OUT);
-    }
-
-    @Test
-    public void onInitialize() {
-        mAppLifecycleListener.initialize();
-        verify(mLifecycleListener).onLifecycleEvent(LifecycleEvent.INITIALIZE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedknowncontent/FeedKnownContentImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedknowncontent/FeedKnownContentImplTest.java
deleted file mode 100644
index fe9b3db..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedknowncontent/FeedKnownContentImplTest.java
+++ /dev/null
@@ -1,203 +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.
-
-package org.chromium.chrome.browser.feed.library.feedknowncontent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.Function;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentMetadata;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentRemoval;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.KnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.OfflineMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.RepresentationData;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link FeedKnownContentImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedKnownContentImplTest {
-    private static final long CONTENT_CREATION_DATE_TIME_MS = 123L;
-    private static final List<ContentRemoval> CONTENT_REMOVED =
-            Collections.singletonList(new ContentRemoval("url", /* requestedByUser= */ false));
-    private static final String URL = "url";
-    private static final String TITLE = "title";
-
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private KnownContent.Listener mListener1;
-    @Mock
-    private KnownContent.Listener mListener2;
-    @Mock
-    private Consumer<List<ContentMetadata>> mKnownContentConsumer;
-    @Mock
-    private ThreadUtils mThreadUtils;
-
-    private final FakeMainThreadRunner mMainThreadRunner =
-            FakeMainThreadRunner.runTasksImmediately();
-
-    @Captor
-    private ArgumentCaptor<Function<StreamPayload, ContentMetadata>> mKnownContentFunctionCaptor;
-
-    @Captor
-    private ArgumentCaptor<Consumer<Result<List<ContentMetadata>>>> mContentMetadataResultCaptor;
-
-    private FeedKnownContentImpl mKnownContentApi;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        when(mThreadUtils.isMainThread()).thenReturn(true);
-        mKnownContentApi =
-                new FeedKnownContentImpl(mFeedSessionManager, mMainThreadRunner, mThreadUtils);
-    }
-
-    @Test
-    public void testSetsListenerOnSessionManager() {
-        verify(mFeedSessionManager)
-                .setKnownContentListener(mKnownContentApi.getKnownContentHostNotifier());
-    }
-
-    @Test
-    public void testNotifyListeners_contentReceived() {
-        mKnownContentApi.addListener(mListener1);
-        mKnownContentApi.addListener(mListener2);
-
-        mKnownContentApi.getKnownContentHostNotifier().onNewContentReceived(
-                /* isNewRefresh= */ false, CONTENT_CREATION_DATE_TIME_MS);
-
-        verify(mListener1)
-                .onNewContentReceived(
-                        /* isNewRefresh= */ false, CONTENT_CREATION_DATE_TIME_MS);
-        verify(mListener2)
-                .onNewContentReceived(
-                        /* isNewRefresh= */ false, CONTENT_CREATION_DATE_TIME_MS);
-    }
-
-    @Test
-    public void testNotifyListeners_contentRemoved() {
-        mKnownContentApi.addListener(mListener1);
-        mKnownContentApi.addListener(mListener2);
-
-        mKnownContentApi.getKnownContentHostNotifier().onContentRemoved(CONTENT_REMOVED);
-
-        verify(mListener1).onContentRemoved(CONTENT_REMOVED);
-        verify(mListener2).onContentRemoved(CONTENT_REMOVED);
-    }
-
-    @Test
-    public void testRemoveListener() {
-        mKnownContentApi.addListener(mListener1);
-        mKnownContentApi.removeListener(mListener1);
-
-        mKnownContentApi.getKnownContentHostNotifier().onContentRemoved(CONTENT_REMOVED);
-        mKnownContentApi.getKnownContentHostNotifier().onNewContentReceived(
-                /* isNewRefresh= */ true, CONTENT_CREATION_DATE_TIME_MS);
-
-        verifyNoMoreInteractions(mListener1);
-    }
-
-    @Test
-    public void testGetKnownContent_returnsNullForNonContent() {
-        mKnownContentApi.getKnownContent(mKnownContentConsumer);
-
-        verify(mFeedSessionManager)
-                .getStreamFeaturesFromHead(mKnownContentFunctionCaptor.capture(),
-                        mContentMetadataResultCaptor.capture());
-
-        assertThat(mKnownContentFunctionCaptor.getValue().apply(StreamPayload.getDefaultInstance()))
-                .isNull();
-    }
-
-    @Test
-    public void testGetKnownContent_returnsContentMetadataFromContent() {
-        mKnownContentApi.getKnownContent(mKnownContentConsumer);
-
-        verify(mFeedSessionManager)
-                .getStreamFeaturesFromHead(mKnownContentFunctionCaptor.capture(),
-                        mContentMetadataResultCaptor.capture());
-
-        StreamPayload streamPayload =
-                StreamPayload.newBuilder()
-                        .setStreamFeature(StreamFeature.newBuilder().setContent(
-                                Content.newBuilder()
-                                        .setOfflineMetadata(
-                                                OfflineMetadata.newBuilder().setTitle(TITLE))
-                                        .setRepresentationData(
-                                                RepresentationData.newBuilder().setUri(URL))))
-                        .build();
-
-        ContentMetadata contentMetadata =
-                mKnownContentFunctionCaptor.getValue().apply(streamPayload);
-
-        assertThat(contentMetadata.getUrl()).isEqualTo(URL);
-        assertThat(contentMetadata.getTitle()).isEqualTo(TITLE);
-    }
-
-    @Test
-    public void testGetKnownContent_failure() {
-        mKnownContentApi.getKnownContent(mKnownContentConsumer);
-
-        verify(mFeedSessionManager)
-                .getStreamFeaturesFromHead(mKnownContentFunctionCaptor.capture(),
-                        mContentMetadataResultCaptor.capture());
-
-        mContentMetadataResultCaptor.getValue().accept(Result.failure());
-
-        verify(mKnownContentConsumer, never())
-                .accept(ArgumentMatchers.<List<ContentMetadata>>any());
-    }
-
-    @Test
-    public void testGetKnownContent_offMainThread() {
-        FakeMainThreadRunner fakeMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-        when(mThreadUtils.isMainThread()).thenReturn(false);
-
-        mKnownContentApi =
-                new FeedKnownContentImpl(mFeedSessionManager, fakeMainThreadRunner, mThreadUtils);
-
-        mKnownContentApi.addListener(mListener1);
-
-        mKnownContentApi.getKnownContentHostNotifier().onNewContentReceived(
-                /* isNewRefresh= */ false, CONTENT_CREATION_DATE_TIME_MS);
-
-        assertThat(fakeMainThreadRunner.hasTasks()).isTrue();
-
-        verifyZeroInteractions(mListener1);
-
-        fakeMainThreadRunner.runAllTasks();
-
-        verify(mListener1)
-                .onNewContentReceived(
-                        /* isNewRefresh= */ false, CONTENT_CREATION_DATE_TIME_MS);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderFactoryTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderFactoryTest.java
deleted file mode 100644
index 57e02aa..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderFactoryTest.java
+++ /dev/null
@@ -1,66 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link FeedModelProviderFactory}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedModelProviderFactoryTest {
-    private final FakeBasicLoggingApi mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-    @Mock
-    private ThreadUtils mThreadUtils;
-    @Mock
-    private TimingUtils mTimingUtils;
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private Configuration mConfig;
-    @Mock
-    private TaskQueue mTaskQueue;
-
-    private MainThreadRunner mMainThreadRunner;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-
-        when(mConfig.getValueOrDefault(ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE, 0L)).thenReturn(0L);
-        when(mConfig.getValueOrDefault(ConfigKey.NON_CACHED_PAGE_SIZE, 0L)).thenReturn(0L);
-        when(mConfig.getValueOrDefault(ConfigKey.NON_CACHED_MIN_PAGE_SIZE, 0L)).thenReturn(0L);
-    }
-
-    @Test
-    public void testModelProviderFactory() {
-        FeedModelProviderFactory factory =
-                new FeedModelProviderFactory(mFeedSessionManager, mThreadUtils, mTimingUtils,
-                        mTaskQueue, mMainThreadRunner, mConfig, mFakeBasicLoggingApi);
-        assertThat(factory.createNew(null, UiContext.getDefaultInstance())).isNotNull();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderTest.java
deleted file mode 100644
index fb41010..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderTest.java
+++ /dev/null
@@ -1,813 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild.Type;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelMutation;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelToken;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompleted;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompletedObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.FeedModelProvider.InitializeModel;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.FeedModelProvider.TokenMutation;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.FeedModelProvider.TokenTracking;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.internal.UpdatableModelChild;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.internal.UpdatableModelToken;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Card;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/** Tests of the {@link FeedModelProvider}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedModelProviderTest {
-    private static final String SESSION_ID = "session";
-
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final String mRootContentId = mIdGenerators.createRootContentId(0);
-    private final FakeBasicLoggingApi mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-
-    @Mock
-    private TaskQueue mTaskQueue;
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private ThreadUtils mThreadUtils;
-    @Mock
-    private Configuration mConfig;
-
-    private ModelChild mContinuationToken;
-    private TimingUtils mTimingUtils = new TimingUtils();
-    private FakeMainThreadRunner mFakeMainThreadRunner;
-
-    private List<PayloadWithId> mChildBindings = new ArrayList<>();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mChildBindings.clear();
-        when(mFeedSessionManager.getStreamFeatures(any()))
-                .thenReturn(Result.success(mChildBindings));
-        mFakeMainThreadRunner = FakeMainThreadRunner.runTasksImmediately();
-    }
-
-    @Test
-    public void testMinimalModelProvider() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        // Add a root to the model provider
-        ModelMutation mutator = modelProvider.edit();
-        assertThat(mutator).isNotNull();
-
-        StreamFeature rootStreamFeature = getRootFeature();
-        mutator.addChild(createStreamStructureAndBinding(rootStreamFeature));
-        mutator.commit();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        // Verify that we have a root
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        assertThat(rootFeature.getStreamFeature()).isEqualTo(rootStreamFeature);
-
-        // Verify that the cursor exists but is at end
-        ModelCursor modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-        assertThat(modelCursor.isAtEnd()).isTrue();
-    }
-
-    @Test
-    public void testRemoveWithAppend() {
-        // Enable synthetic tokens.
-        initSyntheticTokenConfig(
-                /* initialPageSize= */ 10, /* minPageSize= */ 0, /* pageSize= */ 0);
-        FeedModelProvider modelProvider = createFeedModelProvider();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        // Create a root with three children attached.
-        modelProvider.edit()
-                .addChild(createStreamStructureAndBinding(getRootFeature()))
-                .addChild(createStreamStructureAndBinding(createFeature(1, mRootContentId)))
-                .addChild(createStreamStructureAndBinding(createFeature(2, mRootContentId)))
-                .addChild(createStreamStructureAndBinding(createFeature(3, mRootContentId)))
-                .commit();
-
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(1).payload.getStreamFeature());
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(2).payload.getStreamFeature());
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(3).payload.getStreamFeature());
-
-        // Now remove the second and third children, and add more children.
-        modelProvider.edit()
-                .removeChild(createRemove(mRootContentId, createFeatureContentId(2)))
-                .removeChild(createRemove(mRootContentId, createFeatureContentId(3)))
-                .addChild(createStreamStructureAndBinding(createFeature(4, mRootContentId)))
-                .addChild(createStreamStructureAndBinding(createFeature(5, mRootContentId)))
-                .commit();
-
-        // The children should now be #1, #4, and #5; they all should be bound.
-        modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(1).payload.getStreamFeature());
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(4).payload.getStreamFeature());
-        assertThat(modelCursor.getNextItem().getModelFeature().getStreamFeature())
-                .isEqualTo(mChildBindings.get(5).payload.getStreamFeature());
-    }
-
-    @Test
-    public void testEmptyStream() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-
-        modelProvider.edit().commit();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getRootFeature()).isNull();
-    }
-
-    @Test
-    public void testCursor() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        int featureCnt = 2;
-        createTopLevelFeatures(modelProvider, featureCnt).commit();
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-        assertThat(modelCursor.isAtEnd()).isFalse();
-
-        int cnt = 0;
-        while (modelCursor.getNextItem() != null) {
-            cnt++;
-        }
-        assertThat(cnt).isEqualTo(featureCnt);
-        assertThat(modelCursor.isAtEnd()).isTrue();
-    }
-
-    @Test
-    public void testRemove() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        int featureCnt = 2;
-        createTopLevelFeatures(modelProvider, featureCnt)
-                .removeChild(createRemove(mRootContentId, createFeatureContentId(2)))
-                .commit();
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-
-        // Verify that one of the features was removed by a the last operation
-        int cnt = 0;
-        while (modelCursor.getNextItem() != null) {
-            cnt++;
-        }
-        assertThat(cnt).isEqualTo(featureCnt - 1);
-    }
-
-    @Test
-    public void testMultiLevelCursors() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        int featureCnt = 3;
-        ModelMutation mutator = createTopLevelFeatures(modelProvider, featureCnt);
-        String featureParent = createFeatureContentId(2);
-        for (int i = 0; i < featureCnt; i++) {
-            mutator.addChild(createStreamStructureAndBinding(
-                    createFeature(i + 1 + featureCnt, featureParent)));
-        }
-        mutator.commit();
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-        assertThat(modelCursor.isAtEnd()).isFalse();
-
-        int cnt = 0;
-        ModelChild child;
-        while ((child = modelCursor.getNextItem()) != null) {
-            cnt++;
-            if (child.getType() == Type.FEATURE) {
-                ModelFeature feature = child.getModelFeature();
-                ModelCursor nextCursor = feature.getCursor();
-                while (nextCursor.getNextItem() != null) {
-                    cnt++;
-                }
-            }
-        }
-        assertThat(cnt).isEqualTo(featureCnt + featureCnt);
-    }
-
-    @Test
-    public void testSharedState() {
-        ContentId contentId = ContentId.getDefaultInstance();
-        StreamSharedState streamSharedState = StreamSharedState.getDefaultInstance();
-        when(mFeedSessionManager.getSharedState(contentId)).thenReturn(streamSharedState);
-
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        assertThat(modelProvider.getSharedState(contentId)).isEqualTo(streamSharedState);
-    }
-
-    @Test
-    public void testTokenTracking() {
-        UpdatableModelToken continuationToken =
-                new UpdatableModelToken(StreamToken.getDefaultInstance(), false);
-        ArrayList<UpdatableModelChild> location = new ArrayList<>();
-        String parentContentId = "parent.content.id";
-        TokenTracking tokenTracking =
-                new TokenTracking(continuationToken, parentContentId, location);
-        assertThat(tokenTracking.mTokenChild).isEqualTo(continuationToken);
-        assertThat(tokenTracking.mParentContentId).isEqualTo(parentContentId);
-        assertThat(tokenTracking.mLocation).isEqualTo(location);
-    }
-
-    @Test
-    public void testHandleToken() {
-        StreamToken streamToken = StreamToken.getDefaultInstance();
-        ModelToken modelToken = mock(ModelToken.class);
-        when(modelToken.getStreamToken()).thenReturn(streamToken);
-
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        modelProvider.mSessionId = SESSION_ID;
-        modelProvider.handleToken(modelToken);
-        verify(mFeedSessionManager).handleToken(SESSION_ID, streamToken);
-    }
-
-    @Test
-    public void testForceRefresh() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        modelProvider.mSessionId = SESSION_ID;
-        modelProvider.triggerRefresh(RequestReason.OPEN_WITH_CONTENT);
-        assertThat(modelProvider.getDelayedTriggerRefreshForTest()).isFalse();
-        verify(mFeedSessionManager)
-                .triggerRefresh(SESSION_ID, RequestReason.OPEN_WITH_CONTENT,
-                        UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void testForceRefresh_differentReason() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        modelProvider.mSessionId = SESSION_ID;
-        modelProvider.triggerRefresh(RequestReason.HOST_REQUESTED);
-        assertThat(modelProvider.getDelayedTriggerRefreshForTest()).isFalse();
-        verify(mFeedSessionManager)
-                .triggerRefresh(
-                        SESSION_ID, RequestReason.HOST_REQUESTED, UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void testForceRefresh_delayed() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        assertThat(modelProvider.getDelayedTriggerRefreshForTest()).isFalse();
-        assertThat(modelProvider.getRequestReasonForTest()).isEqualTo(RequestReason.UNKNOWN);
-        modelProvider.triggerRefresh(RequestReason.HOST_REQUESTED);
-        assertThat(modelProvider.getDelayedTriggerRefreshForTest()).isTrue();
-        assertThat(modelProvider.getRequestReasonForTest()).isEqualTo(RequestReason.HOST_REQUESTED);
-        verify(mFeedSessionManager, never())
-                .triggerRefresh(eq(null), anyInt(), any(UiContext.class));
-    }
-
-    @Test
-    public void testInvalidate() {
-        // Create a valid model
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        StreamFeature rootStreamFeature = getRootFeature();
-        modelProvider.edit().addChild(createStreamStructureAndBinding(rootStreamFeature)).commit();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-
-        modelProvider.invalidate();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-        assertThat(modelCursor.isAtEnd()).isTrue();
-    }
-
-    @Test
-    public void testInvalidate_unregister() {
-        mFakeMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        mFakeMainThreadRunner.runAllTasks();
-
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        modelProvider.invalidate();
-        modelProvider.unregisterObserver(observer);
-        mFakeMainThreadRunner.runAllTasks();
-        verify(observer, never()).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void testObserverLifecycle() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelProviderObserver observer1 = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer1);
-        verify(observer1, never()).onSessionStart(UiContext.getDefaultInstance());
-
-        StreamFeature rootStreamFeature = getRootFeature();
-        modelProvider.edit().addChild(createStreamStructureAndBinding(rootStreamFeature)).commit();
-        verify(observer1).onSessionStart(UiContext.getDefaultInstance());
-
-        ModelProviderObserver observer2 = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer2);
-        verify(observer2).onSessionStart(UiContext.getDefaultInstance());
-
-        modelProvider.invalidate();
-        verify(observer1).onSessionFinished(UiContext.getDefaultInstance());
-        verify(observer2).onSessionFinished(UiContext.getDefaultInstance());
-
-        ModelProviderObserver observer3 = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer3);
-        verify(observer3).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void testObserverLifecycle_resetRoot() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        verify(observer, never()).onSessionStart(UiContext.getDefaultInstance());
-
-        StreamFeature rootStreamFeature = getRootFeature();
-        modelProvider.edit().addChild(createStreamStructureAndBinding(rootStreamFeature)).commit();
-        verify(observer).onSessionStart(UiContext.getDefaultInstance());
-
-        String anotherRootId = mIdGenerators.createRootContentId(100);
-        rootStreamFeature = StreamFeature.newBuilder().setContentId(anotherRootId).build();
-        modelProvider.edit().addChild(createStreamStructureAndBinding(rootStreamFeature)).commit();
-        verify(observer).onSessionFinished(UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-    }
-
-    @Test
-    public void testObserverList() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-
-        Collection<ModelProviderObserver> observers = modelProvider.getObserversToNotify();
-        assertThat(observers.size()).isEqualTo(1);
-        assertThat(observers).contains(observer);
-    }
-
-    @Test
-    public void testRaiseError_noCards() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-
-        ModelError modelError = new ModelError(ErrorType.NO_CARDS_ERROR, null);
-        modelProvider.raiseError(modelError);
-        verify(observer).onError(modelError);
-    }
-
-    @Test
-    public void testRaiseError_pagination() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-
-        int featureCnt = 3;
-        StreamToken streamToken = initializeStreamWithToken(modelProvider, featureCnt);
-        ModelChild tokenChild = getContinuationToken(modelProvider.getRootFeature());
-        assertThat(tokenChild).isNotNull();
-
-        ModelError modelError =
-                new ModelError(ErrorType.PAGINATION_ERROR, streamToken.getNextPageToken());
-        TokenCompletedObserver observer = mock(TokenCompletedObserver.class);
-        tokenChild.getModelToken().registerObserver(observer);
-        modelProvider.raiseError(modelError);
-        verify(observer).onError(modelError);
-    }
-
-    @Test
-    public void testInitializationModelMutationHandler() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        InitializeModel initializeModel =
-                modelProvider.new InitializeModel(UiContext.getDefaultInstance());
-        initializeModel.postMutation();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        verify(observer).onSessionStart(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void testTokens() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-
-        int featureCnt = 3;
-        StreamToken streamToken = initializeStreamWithToken(modelProvider, featureCnt);
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        int cnt = 0;
-        mContinuationToken = null;
-        ModelChild child;
-        while ((child = modelCursor.getNextItem()) != null) {
-            cnt++;
-            if (child.getType() == Type.TOKEN) {
-                mContinuationToken = child;
-            }
-        }
-        assertThat(cnt).isEqualTo(featureCnt + 1);
-        assertThat(mContinuationToken).isNotNull();
-        assertThat(mContinuationToken.getModelToken().getStreamToken()).isEqualTo(streamToken);
-    }
-
-    @Test
-    public void testTokenMutation() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-
-        int featureCnt = 3;
-        StreamToken streamToken = initializeStreamWithToken(modelProvider, featureCnt);
-        Map<ByteString, TokenTracking> tokens = modelProvider.getTokensForTest();
-        assertThat(tokens).hasSize(1);
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        mContinuationToken = null;
-        ModelChild child;
-        while ((child = modelCursor.getNextItem()) != null) {
-            if (child.getType() == Type.TOKEN) {
-                mContinuationToken = child;
-            }
-        }
-        assertThat(mContinuationToken).isNotNull();
-
-        TokenMutation tokenMutation = modelProvider.new TokenMutation(streamToken);
-        TokenTracking tokenTracking = tokenMutation.getTokenTrackingForTest();
-        assertThat(tokenTracking.mLocation.size()).isEqualTo(featureCnt + 1);
-
-        tokenMutation.preMutation();
-        assertThat(tokenTracking.mLocation.size()).isEqualTo(featureCnt + 1);
-        assertThat(tokenMutation.mNewCursorStart).isEqualTo(tokenTracking.mLocation.size() - 1);
-        tokens = modelProvider.getTokensForTest();
-        assertThat(tokens).hasSize(0);
-
-        TokenCompletedObserver tokenCompletedObserver = new TokenCompletedObserver() {
-            @Override
-            public void onTokenCompleted(TokenCompleted tokenCompleted) {
-                assertThat(tokenCompleted).isNotNull();
-            }
-
-            @Override
-            public void onError(ModelError error) {}
-        };
-        mContinuationToken.getModelToken().registerObserver(tokenCompletedObserver);
-        tokenMutation.postMutation();
-    }
-
-    @Test
-    public void testSyntheticToken() {
-        int initialPageSize = 4;
-        int pageSize = 4;
-        initSyntheticTokenConfig(
-                /* initialPageSize= */ initialPageSize, /* minPageSize= */ 2,
-                /* pageSize= */ pageSize);
-
-        doAnswer(invocation -> {
-            Object[] args = invocation.getArguments();
-            ((Runnable) args[2]).run();
-            return null;
-        })
-                .when(mTaskQueue)
-                .execute(anyInt(), anyInt(), any());
-
-        FeedModelProvider modelProvider = createFeedModelProvider();
-        int featureCnt = 11;
-        createTopLevelFeatures(modelProvider, featureCnt).commit();
-
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-
-        ModelChild lastChild = assertCursorSize(modelCursor, initialPageSize + 1);
-        assertThat(lastChild.getType()).isEqualTo(Type.TOKEN);
-        assertThat(lastChild.getModelToken().isSynthetic()).isTrue();
-
-        // The token should be handled in the FeedModelProvider
-        modelProvider.handleToken(lastChild.getModelToken());
-        modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-
-        lastChild = assertCursorSize(modelCursor, initialPageSize + pageSize + 1);
-        assertThat(lastChild.getType()).isEqualTo(Type.TOKEN);
-        assertThat(lastChild.getModelToken().isSynthetic()).isTrue();
-
-        // last page
-        modelProvider.handleToken(lastChild.getModelToken());
-        modelCursor = rootFeature.getCursor();
-        assertThat(modelCursor).isNotNull();
-
-        lastChild = assertCursorSize(modelCursor, featureCnt);
-        assertThat(lastChild.getType()).isEqualTo(Type.FEATURE);
-    }
-
-    @Test
-    public void testSyntheticToken_missingToken() {
-        int initialPageSize = 4;
-        int pageSize = 4;
-        initSyntheticTokenConfig(
-                /* initialPageSize= */ initialPageSize, /* minPageSize= */ 2,
-                /* pageSize= */ pageSize);
-
-        doAnswer(invocation -> {
-            Object[] args = invocation.getArguments();
-            ((Runnable) args[2]).run();
-            return null;
-        })
-                .when(mTaskQueue)
-                .execute(anyInt(), anyInt(), any());
-
-        FeedModelProvider modelProvider = createFeedModelProvider();
-        createTopLevelFeatures(modelProvider, /* featureCount= */ 11).commit();
-
-        ModelCursor modelCursor = modelProvider.getRootFeature().getCursor();
-        ModelChild lastChild = assertCursorSize(modelCursor, initialPageSize + 1);
-        assertThat(lastChild.getType()).isEqualTo(Type.TOKEN);
-        assertThat(lastChild.getModelToken().isSynthetic()).isTrue();
-
-        // Simulate removing the synthetic token from the root.
-        FakeTokenCompletedObserver observer = new FakeTokenCompletedObserver();
-        lastChild.getModelToken().registerObserver(observer);
-        modelProvider.clearRootChildrenForTest();
-        modelProvider.handleToken(lastChild.getModelToken());
-        assertThat(observer.mErrorThrown).isNotNull();
-        assertThat(observer.mErrorThrown.getErrorType()).isEqualTo(ErrorType.SYNTHETIC_TOKEN_ERROR);
-    }
-
-    @Test
-    public void testViewDepthProvider_null() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        assertThat(modelProvider.getViewDepthProvider(null)).isNull();
-    }
-
-    @Test
-    public void testViewDepthProvider_delegatedNull() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ViewDepthProvider mockProvider = mock(ViewDepthProvider.class);
-        when(mockProvider.getChildViewDepth()).thenReturn(null);
-        assertThat(modelProvider.getViewDepthProvider(null)).isNull();
-    }
-
-    @Test
-    public void testViewDepthProvider_delegated() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        int featureCnt = 3;
-        ModelMutation mutator = createTopLevelFeatures(modelProvider, featureCnt);
-        String featureParent = createFeatureContentId(2);
-        for (int i = 0; i < featureCnt; i++) {
-            mutator.addChild(createStreamStructureAndBinding(
-                    createFeature(i + 1 + featureCnt, featureParent)));
-        }
-        String childFeatureContentId = createFeatureContentId(1 + featureCnt);
-        mutator.commit();
-
-        ViewDepthProvider mockProvider = mock(ViewDepthProvider.class);
-        when(mockProvider.getChildViewDepth()).thenReturn(childFeatureContentId);
-
-        ViewDepthProvider provider = modelProvider.getViewDepthProvider(mockProvider);
-        assertThat(provider.getChildViewDepth()).isEqualTo(featureParent);
-    }
-
-    @Test
-    public void testGetEmptyRoot() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        ModelMutation mutation = getRootedModelMutator(modelProvider);
-        mChildBindings.clear();
-        mutation.commit();
-
-        assertThat(modelProvider.getRootFeature()).isNull();
-        assertThat(mFakeBasicLoggingApi.lastInternalError)
-                .isEqualTo(InternalFeedError.ROOT_NOT_BOUND_TO_FEATURE);
-    }
-
-    @Test
-    public void testGetAllRootChildren() {
-        FeedModelProvider modelProvider = createFeedModelProviderWithConfig();
-        int featureCnt = 3;
-        createTopLevelFeatures(modelProvider, featureCnt).commit();
-
-        List<ModelChild> rootChildren = modelProvider.getAllRootChildren();
-        assertThat(rootChildren).hasSize(featureCnt);
-    }
-
-    private ModelChild getContinuationToken(ModelFeature rootFeature) {
-        ModelCursor modelCursor = rootFeature.getCursor();
-        ModelChild token = null;
-        ModelChild child;
-        while ((child = modelCursor.getNextItem()) != null) {
-            if (child.getType() == Type.TOKEN) {
-                token = child;
-            }
-        }
-        assertThat(token).isNotNull();
-        return token;
-    }
-
-    private ModelChild assertCursorSize(ModelCursor cursor, int size) {
-        int cnt = 0;
-        ModelChild lastChild = null;
-        ModelChild child;
-        while ((child = cursor.getNextItem()) != null) {
-            cnt++;
-            lastChild = child;
-        }
-        assertThat(cnt).isEqualTo(size);
-        assertThat(lastChild).isNotNull();
-        return lastChild;
-    }
-
-    private void initDefaultConfig() {
-        initSyntheticTokenConfig(
-                /* initialPageSize= */ 0L, /* minPageSize= */ 0L, /* pageSize= */ 0L);
-    }
-
-    private void initSyntheticTokenConfig(long initialPageSize, long minPageSize, long pageSize) {
-        when(mConfig.getValueOrDefault(ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE, 0L))
-                .thenReturn(initialPageSize);
-        when(mConfig.getValueOrDefault(ConfigKey.NON_CACHED_PAGE_SIZE, 0L)).thenReturn(pageSize);
-        when(mConfig.getValueOrDefault(ConfigKey.NON_CACHED_MIN_PAGE_SIZE, 0L))
-                .thenReturn(minPageSize);
-    }
-
-    private StreamToken initializeStreamWithToken(FeedModelProvider modelProvider, int featureCnt) {
-        ModelMutation mutator = createTopLevelFeatures(modelProvider, featureCnt);
-
-        // Populate the model provider with a continuation token at the end.
-        ByteString bytes = ByteString.copyFrom("continuation", Charset.defaultCharset());
-        StreamToken streamToken = StreamToken.newBuilder()
-                                          .setNextPageToken(bytes)
-                                          .setParentId(mRootContentId)
-                                          .build();
-        mutator.addChild(createStreamStructureAndBinding(streamToken));
-        mutator.commit();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        return streamToken;
-    }
-
-    private FeedModelProvider createFeedModelProviderWithConfig() {
-        initDefaultConfig();
-        return createFeedModelProvider();
-    }
-
-    private FeedModelProvider createFeedModelProvider() {
-        return new FeedModelProvider(mFeedSessionManager, mThreadUtils, mTimingUtils, mTaskQueue,
-                mFakeMainThreadRunner, null, mConfig, mFakeBasicLoggingApi);
-    }
-
-    private ModelMutation createTopLevelFeatures(ModelProvider modelProvider, int featureCount) {
-        ModelMutation mutator = getRootedModelMutator(modelProvider);
-        for (int i = 0; i < featureCount; i++) {
-            mutator.addChild(createStreamStructureAndBinding(createFeature(i + 1, mRootContentId)));
-        }
-        return mutator;
-    }
-
-    private StreamFeature createFeature(int i, String parentContentId) {
-        return StreamFeature.newBuilder()
-                .setParentId(parentContentId)
-                .setContentId(createFeatureContentId(i))
-                .setCard(Card.getDefaultInstance())
-                .build();
-    }
-
-    private String createFeatureContentId(int i) {
-        return mIdGenerators.createFeatureContentId(i);
-    }
-
-    private ModelMutation getRootedModelMutator(ModelProvider modelProvider) {
-        ModelMutation mutator = modelProvider.edit();
-        assertThat(mutator).isNotNull();
-        StreamFeature rootStreamFeature = getRootFeature();
-        mutator.addChild(createStreamStructureAndBinding(rootStreamFeature));
-        return mutator;
-    }
-
-    private StreamStructure createStreamStructureFromFeature(StreamFeature feature) {
-        StreamStructure.Builder builder = StreamStructure.newBuilder()
-                                                  .setContentId(feature.getContentId())
-                                                  .setOperation(Operation.UPDATE_OR_APPEND);
-        if (feature.hasParentId()) {
-            builder.setParentContentId(feature.getParentId());
-        }
-        return builder.build();
-    }
-
-    private StreamStructure createStreamStructureFromToken(StreamToken token) {
-        StreamStructure.Builder builder = StreamStructure.newBuilder()
-                                                  .setContentId(token.getContentId())
-                                                  .setOperation(Operation.UPDATE_OR_APPEND);
-        if (token.hasParentId()) {
-            builder.setParentContentId(token.getParentId());
-        }
-        return builder.build();
-    }
-
-    private StreamStructure createRemove(String parentContentId, String contentId) {
-        return StreamStructure.newBuilder()
-                .setContentId(contentId)
-                .setParentContentId(parentContentId)
-                .setOperation(Operation.REMOVE)
-                .build();
-    }
-
-    /**
-     * This has the side affect of populating {@code childBindings} with a {@code PayloadWithId}.
-     */
-    private StreamStructure createStreamStructureAndBinding(StreamFeature feature) {
-        StreamPayload payload = StreamPayload.newBuilder().setStreamFeature(feature).build();
-        mChildBindings.add(new PayloadWithId(feature.getContentId(), payload));
-        return createStreamStructureFromFeature(feature);
-    }
-
-    /**
-     * This has the side affect of populating {@code childBindings} with a {@code PayloadWithId}.
-     */
-    private StreamStructure createStreamStructureAndBinding(StreamToken token) {
-        StreamPayload payload = StreamPayload.newBuilder().setStreamToken(token).build();
-        mChildBindings.add(new PayloadWithId(token.getContentId(), payload));
-        return createStreamStructureFromToken(token);
-    }
-
-    private StreamFeature getRootFeature() {
-        return StreamFeature.newBuilder().setContentId(mRootContentId).build();
-    }
-
-    private static class FakeTokenCompletedObserver implements TokenCompletedObserver {
-        private ModelError mErrorThrown;
-
-        @Override
-        public void onTokenCompleted(TokenCompleted tokenCompleted) {}
-
-        @Override
-        public void onError(ModelError modelError) {
-            mErrorThrown = modelError;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/FeatureChangeImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/FeatureChangeImplTest.java
deleted file mode 100644
index 9c1bc6b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/FeatureChangeImplTest.java
+++ /dev/null
@@ -1,75 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link FeatureChangeImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeatureChangeImplTest {
-    @Mock
-    private ModelFeature mModelFeature;
-
-    private String mModelContentId;
-    private ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mModelContentId = mIdGenerators.createFeatureContentId(1);
-        StreamFeature streamFeature =
-                StreamFeature.newBuilder().setContentId(mModelContentId).build();
-        when(mModelFeature.getStreamFeature()).thenReturn(streamFeature);
-    }
-
-    @Test
-    public void testStreamFeatureChange() {
-        FeatureChangeImpl featureChange = new FeatureChangeImpl(mModelFeature);
-
-        assertThat(featureChange.getModelFeature()).isEqualTo(mModelFeature);
-        assertThat(featureChange.getContentId()).isEqualTo(mModelContentId);
-        assertThat(featureChange.getChildChanges().getAppendedChildren()).isEmpty();
-        assertThat(featureChange.getChildChanges().getRemovedChildren()).isEmpty();
-        assertThat(featureChange.isFeatureChanged()).isFalse();
-
-        featureChange.setFeatureChanged(true);
-        assertThat(featureChange.isFeatureChanged()).isTrue();
-    }
-
-    @Test
-    public void testAppendChild() {
-        ModelChild modelChild = mock(ModelChild.class);
-        FeatureChangeImpl featureChange = new FeatureChangeImpl(mModelFeature);
-        featureChange.getChildChangesImpl().addAppendChild(modelChild);
-        assertThat(featureChange.getChildChanges().getAppendedChildren()).hasSize(1);
-        assertThat(featureChange.getChildChanges().getAppendedChildren()).contains(modelChild);
-    }
-
-    @Test
-    public void testRemoveChild() {
-        ModelChild modelChild = mock(ModelChild.class);
-        FeatureChangeImpl featureChange = new FeatureChangeImpl(mModelFeature);
-        featureChange.getChildChangesImpl().removeChild(modelChild);
-        assertThat(featureChange.getChildChanges().getRemovedChildren()).hasSize(1);
-        assertThat(featureChange.getChildChanges().getRemovedChildren()).contains(modelChild);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelCursorImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelCursorImplTest.java
deleted file mode 100644
index fa646de..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelCursorImplTest.java
+++ /dev/null
@@ -1,132 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.internal.ModelCursorImpl.CursorIterator;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link ModelCursorImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ModelCursorImplTest {
-    private List<UpdatableModelChild> mModelChildren;
-    private String mParentContentId;
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mModelChildren = new ArrayList<>();
-        mParentContentId = "parent.content.id";
-    }
-
-    @Test
-    public void testEmptyCursor() {
-        ModelCursorImpl cursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        assertThat(cursor.isAtEnd()).isTrue();
-    }
-
-    @Test
-    public void testReleaseCursor() {
-        mModelChildren.add(new UpdatableModelChild("contentId", "parentId"));
-        ModelCursorImpl cursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        assertThat(cursor.isAtEnd()).isFalse();
-
-        cursor.release();
-        assertThat(cursor.isAtEnd()).isTrue();
-
-        assertThat(cursor.getNextItem()).isNull();
-    }
-
-    @Test
-    public void testIteration() {
-        int childrenToAdd = 3;
-        for (int i = 0; i < childrenToAdd; i++) {
-            mModelChildren.add(new UpdatableModelChild("contentId", "parentId"));
-        }
-        ModelCursorImpl cursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        int childCount = 0;
-        for (int i = 0; i < childrenToAdd; i++) {
-            childCount++;
-            assertThat(cursor.getNextItem()).isNotNull();
-        }
-        assertThat(cursor.getNextItem()).isNull();
-        assertThat(cursor.isAtEnd()).isTrue();
-        assertThat(childCount).isEqualTo(childrenToAdd);
-    }
-
-    @Test
-    public void testUpdateIterator_append() {
-        int childrenToAdd = 3;
-        for (int i = 0; i < childrenToAdd; i++) {
-            mModelChildren.add(new UpdatableModelChild("contentId", "parentId"));
-        }
-        ModelCursorImpl cursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        ModelFeature modelFeature = mock(ModelFeature.class);
-        FeatureChangeImpl featureChange = new FeatureChangeImpl(modelFeature);
-        UpdatableModelChild newChild = new UpdatableModelChild("contentId", "parentId");
-        featureChange.getChildChangesImpl().addAppendChild(newChild);
-        cursor.updateIterator(featureChange);
-        List<UpdatableModelChild> children = cursor.getChildListForTesting();
-        assertThat(children).hasSize(4);
-        assertThat(children.get(3)).isEqualTo(newChild);
-    }
-
-    @Test
-    public void testUpdateIterator_remove() {
-        int childrenToAdd = 3;
-        for (int i = 0; i < childrenToAdd; i++) {
-            String contentId = mContentIdGenerators.createFeatureContentId(i);
-            UpdatableModelChild child = new UpdatableModelChild(contentId, "parentId");
-            mModelChildren.add(child);
-        }
-        ModelCursorImpl cursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        ModelFeature modelFeature = mock(ModelFeature.class);
-        FeatureChangeImpl featureChange = new FeatureChangeImpl(modelFeature);
-        featureChange.getChildChangesImpl().removeChild(mModelChildren.get(1));
-        cursor.updateIterator(featureChange);
-        List<UpdatableModelChild> children = cursor.getChildListForTesting();
-        assertThat(children).hasSize(2);
-    }
-
-    @Test
-    public void testCursorIterator() {
-        int childrenToAdd = 3;
-        for (int i = 0; i < childrenToAdd; i++) {
-            mModelChildren.add(new UpdatableModelChild("contentId", "parentId"));
-        }
-        CursorIterator cursorIterator =
-                new ModelCursorImpl(mParentContentId, mModelChildren).new CursorIterator();
-        assertThat(cursorIterator.getPosition()).isEqualTo(0);
-        assertThat(cursorIterator.hasNext()).isTrue();
-        assertThat(cursorIterator.next()).isEqualTo(mModelChildren.get(0));
-        assertThat(cursorIterator.getPosition()).isEqualTo(1);
-    }
-
-    @Test
-    public void testCopiesList() {
-        UpdatableModelChild child1 = new UpdatableModelChild("content id", null);
-        mModelChildren.add(child1);
-        ModelCursorImpl modelCursor = new ModelCursorImpl(mParentContentId, mModelChildren);
-        UpdatableModelChild child2 = new UpdatableModelChild("content id 2", null);
-        mModelChildren.add(child2);
-        assertThat(modelCursor.getChildListForTesting()).containsExactly(child1);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelFeatureImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelFeatureImplTest.java
deleted file mode 100644
index 516becf..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelFeatureImplTest.java
+++ /dev/null
@@ -1,47 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link UpdatableModelFeature}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ModelFeatureImplTest {
-    private StreamFeature mStreamFeature;
-    @Mock
-    private CursorProvider mCursorProvider;
-    @Mock
-    private ModelCursor mModelCursor;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        String contentId = "content-id";
-        mStreamFeature = StreamFeature.newBuilder().setContentId(contentId).build();
-        when(mCursorProvider.getCursor(contentId)).thenReturn(mModelCursor);
-    }
-
-    @Test
-    public void testBase() {
-        UpdatableModelFeature modelFeature =
-                new UpdatableModelFeature(mStreamFeature, mCursorProvider);
-        assertThat(modelFeature.getStreamFeature()).isEqualTo(mStreamFeature);
-        assertThat(modelFeature.getCursor()).isEqualTo(mModelCursor);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelMutationImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelMutationImplTest.java
deleted file mode 100644
index 515cf99..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelMutationImplTest.java
+++ /dev/null
@@ -1,97 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.common.functional.Committer;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.internal.ModelMutationImpl.Change;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of {@link ModelMutationImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ModelMutationImplTest {
-    @Mock
-    private Committer<Void, Change> mCommitter;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-    }
-
-    @Test
-    public void testFeature() {
-        ModelMutationImpl modelMutator = new ModelMutationImpl(mCommitter);
-        StreamFeature streamFeature = StreamFeature.newBuilder().build();
-        modelMutator.addChild(mCreateStreamStructureFromFeature(streamFeature));
-        assertThat(modelMutator.mChange.mStructureChanges).hasSize(1);
-        modelMutator.commit();
-        verify(mCommitter).commit(modelMutator.mChange);
-    }
-
-    @Test
-    public void testToken() {
-        ModelMutationImpl modelMutator = new ModelMutationImpl(mCommitter);
-        StreamToken streamToken = StreamToken.newBuilder().build();
-        modelMutator.addChild(mCreateStreamStructureFromToken(streamToken));
-        assertThat(modelMutator.mChange.mStructureChanges).hasSize(1);
-        modelMutator.commit();
-        verify(mCommitter).commit(modelMutator.mChange);
-    }
-
-    @Test
-    public void testMutationContext() {
-        ModelMutationImpl modelMutator = new ModelMutationImpl(mCommitter);
-        MutationContext mutationContext = MutationContext.EMPTY_CONTEXT;
-        modelMutator.setMutationContext(mutationContext);
-        assertThat(modelMutator.mChange.mStructureChanges).isEmpty();
-        modelMutator.commit();
-        verify(mCommitter).commit(modelMutator.mChange);
-        assertThat(modelMutator.mChange.mMutationContext).isEqualTo(mutationContext);
-    }
-
-    @Test
-    public void testRemove() {
-        ModelMutationImpl modelMutator = new ModelMutationImpl(mCommitter);
-        assertThat(modelMutator.mChange.mStructureChanges).isEmpty();
-        modelMutator.removeChild(StreamStructure.getDefaultInstance());
-        assertThat(modelMutator.mChange.mStructureChanges).hasSize(1);
-    }
-
-    private StreamStructure mCreateStreamStructureFromFeature(StreamFeature feature) {
-        StreamStructure.Builder builder = StreamStructure.newBuilder()
-                                                  .setContentId(feature.getContentId())
-                                                  .setOperation(Operation.UPDATE_OR_APPEND);
-        if (feature.hasParentId()) {
-            builder.setParentContentId(feature.getParentId());
-        }
-        return builder.build();
-    }
-
-    private StreamStructure mCreateStreamStructureFromToken(StreamToken token) {
-        StreamStructure.Builder builder = StreamStructure.newBuilder()
-                                                  .setContentId(token.getContentId())
-                                                  .setOperation(Operation.UPDATE_OR_APPEND);
-        if (token.hasParentId()) {
-            builder.setParentContentId(token.getParentId());
-        }
-        return builder.build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelTokenImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelTokenImplTest.java
deleted file mode 100644
index b0d6dac..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelTokenImplTest.java
+++ /dev/null
@@ -1,51 +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.
-
-package org.chromium.chrome.browser.feed.library.feedmodelprovider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompletedObserver;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests of {@link UpdatableModelToken}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ModelTokenImplTest {
-    @Mock
-    private TokenCompletedObserver mChangeObserver;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-    }
-
-    @Test
-    public void testFeature() {
-        StreamToken token = StreamToken.newBuilder().build();
-        UpdatableModelToken modelToken = new UpdatableModelToken(token, false);
-        assertThat(modelToken.getStreamToken()).isEqualTo(token);
-    }
-
-    @Test
-    public void testChangeObserverList() {
-        StreamToken token = StreamToken.newBuilder().build();
-        UpdatableModelToken modelToken = new UpdatableModelToken(token, false);
-        modelToken.registerObserver(mChangeObserver);
-        List<TokenCompletedObserver> observers = modelToken.getObserversToNotify();
-        assertThat(observers.size()).isEqualTo(1);
-        assertThat(observers.get(0)).isEqualTo(mChangeObserver);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/FeedProtocolAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/FeedProtocolAdapterTest.java
deleted file mode 100644
index 1430452..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/FeedProtocolAdapterTest.java
+++ /dev/null
@@ -1,235 +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.
-
-package org.chromium.chrome.browser.feed.library.feedprotocoladapter;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.collect.ImmutableList;
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.RequiredContentAdapter;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-import java.util.List;
-
-/** Tests of the {@link FeedProtocolAdapter} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedProtocolAdapterTest {
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private final FeedProtocolAdapter mProtocolAdapter =
-            new FeedProtocolAdapter(ImmutableList.of(), mTimingUtils);
-
-    @Mock
-    private RequiredContentAdapter mAdapter;
-    private ResponseBuilder mResponseBuilder;
-
-    @Before
-    public void init() {
-        initMocks(this);
-        mResponseBuilder = new ResponseBuilder();
-    }
-
-    @Test
-    public void testConvertContentId() {
-        ContentId contentId = ResponseBuilder.createFeatureContentId(13);
-        String streamContentId = mProtocolAdapter.getStreamContentId(contentId);
-        assertThat(streamContentId).isNotNull();
-        assertThat(streamContentId).contains(contentId.getContentDomain());
-        assertThat(streamContentId).contains(Long.toString(contentId.getId()));
-        assertThat(streamContentId).contains(contentId.getTable());
-    }
-
-    @Test
-    public void testConvertContentId_malformed_notThreeParts() {
-        String streamContentId = "test" + FeedProtocolAdapter.CONTENT_ID_DELIMITER + "break";
-        Result<ContentId> contentIdResult = mProtocolAdapter.getWireContentId(streamContentId);
-        assertThat(contentIdResult.isSuccessful()).isFalse();
-    }
-
-    @Test
-    public void testConvertContentId_malformed_nonNumericId() {
-        String streamContentId = "test" + FeedProtocolAdapter.CONTENT_ID_DELIMITER + "break"
-                + FeedProtocolAdapter.CONTENT_ID_DELIMITER + "test";
-        Result<ContentId> contentIdResult = mProtocolAdapter.getWireContentId(streamContentId);
-        assertThat(contentIdResult.isSuccessful()).isFalse();
-    }
-
-    @Test
-    public void testConvertContentId_roundTrip() {
-        ContentId contentId = ResponseBuilder.createFeatureContentId(13);
-        String streamContentId = mProtocolAdapter.getStreamContentId(contentId);
-        Result<ContentId> contentIdResult = mProtocolAdapter.getWireContentId(streamContentId);
-        assertThat(contentIdResult.isSuccessful()).isTrue();
-        assertThat(contentIdResult.getValue()).isNotNull();
-        assertThat(contentIdResult.getValue()).isEqualTo(contentId);
-    }
-
-    @Test
-    public void testConvertContentId_roundTrip_partialContentId() {
-        ContentId contentId = ContentId.newBuilder().setId(13).build();
-        String streamContentId = mProtocolAdapter.getStreamContentId(contentId);
-        Result<ContentId> contentIdResult = mProtocolAdapter.getWireContentId(streamContentId);
-        assertThat(contentIdResult.isSuccessful()).isTrue();
-        assertThat(contentIdResult.getValue()).isNotNull();
-        assertThat(contentIdResult.getValue()).isEqualTo(contentId);
-    }
-
-    @Test
-    public void testSimpleResponse_clear() {
-        Response response = mResponseBuilder.addClearOperation().build();
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue().streamDataOperations).hasSize(1);
-    }
-
-    @Test
-    public void testSimpleResponse_feature() {
-        Response response = mResponseBuilder.addRootFeature().build();
-
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue().streamDataOperations).hasSize(1);
-
-        StreamDataOperation sdo = results.getValue().streamDataOperations.get(0);
-        assertThat(sdo.hasStreamPayload()).isTrue();
-        assertThat(sdo.getStreamPayload().hasStreamFeature()).isTrue();
-        assertThat(sdo.hasStreamStructure()).isTrue();
-        assertThat(sdo.getStreamStructure().hasContentId()).isTrue();
-        // Added the root
-        assertThat(sdo.getStreamStructure().hasParentContentId()).isFalse();
-    }
-
-    @Test
-    public void testSimpleResponse_feature_semanticProperties() {
-        ContentId contentId = ResponseBuilder.createFeatureContentId(13);
-        ByteString semanticData = ByteString.copyFromUtf8("helloWorld");
-        Response response =
-                new ResponseBuilder().addCardWithSemanticData(contentId, semanticData).build();
-
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        // Note that 2 operations are created (the card and the semantic data). We want the latter.
-        assertThat(results.getValue().streamDataOperations).hasSize(2);
-        StreamDataOperation sdo = results.getValue().streamDataOperations.get(1);
-        assertThat(sdo.getStreamPayload().hasSemanticData()).isTrue();
-        assertThat(sdo.getStreamPayload().getSemanticData()).isEqualTo(semanticData);
-    }
-
-    @Test
-    public void testResponse_rootClusterCardContent() {
-        ContentId rootId = ContentId.newBuilder().setId(1).build();
-        ContentId clusterId = ContentId.newBuilder().setId(2).build();
-        ContentId cardId = ContentId.newBuilder().setId(3).build();
-        Response response = mResponseBuilder.addRootFeature(rootId)
-                                    .addClusterFeature(clusterId, rootId)
-                                    .addCard(cardId, clusterId)
-                                    .build();
-
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        List<StreamDataOperation> operations = results.getValue().streamDataOperations;
-
-        assertThat(operations).hasSize(4);
-        assertThat(operations.get(0).getStreamPayload().getStreamFeature().hasStream()).isTrue();
-        assertThat(operations.get(1).getStreamPayload().getStreamFeature().hasCluster()).isTrue();
-        assertThat(operations.get(2).getStreamPayload().getStreamFeature().hasCard()).isTrue();
-        assertThat(operations.get(3).getStreamPayload().getStreamFeature().hasContent()).isTrue();
-    }
-
-    @Test
-    public void testResponse_remove() {
-        Response response = mResponseBuilder
-                                    .removeFeature(ContentId.getDefaultInstance(),
-                                            ContentId.getDefaultInstance())
-                                    .build();
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue().streamDataOperations).hasSize(1);
-    }
-
-    @Test
-    public void testPietSharedState() {
-        Response response = mResponseBuilder.addPietSharedState().build();
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue().streamDataOperations).hasSize(1);
-        StreamDataOperation sdo = results.getValue().streamDataOperations.get(0);
-        assertThat(sdo.hasStreamPayload()).isTrue();
-        assertThat(sdo.getStreamPayload().hasStreamSharedState()).isTrue();
-        assertThat(sdo.hasStreamStructure()).isTrue();
-        assertThat(sdo.getStreamStructure().hasContentId()).isTrue();
-    }
-
-    @Test
-    public void testContinuationToken_nextPageToken() {
-        ByteString tokenForMutation = ByteString.copyFrom("token", Charset.defaultCharset());
-        Response response = mResponseBuilder.addStreamToken(1, tokenForMutation).build();
-
-        Result<Model> results = mProtocolAdapter.createModel(response);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue().streamDataOperations).hasSize(1);
-        StreamDataOperation sdo = results.getValue().streamDataOperations.get(0);
-        assertThat(sdo.hasStreamPayload()).isTrue();
-        assertThat(sdo.getStreamPayload().hasStreamToken()).isTrue();
-    }
-
-    @Test
-    public void testRequiredContentAdapter() {
-        when(mAdapter.determineRequiredContentIds(any(DataOperation.class)))
-                .thenReturn(ImmutableList.of(ContentId.newBuilder().setId(1).build()))
-                .thenReturn(ImmutableList.of(ContentId.newBuilder().setId(2).build()))
-                .thenReturn(ImmutableList.of(ContentId.newBuilder().setId(1).build(),
-                        ContentId.newBuilder().setId(2).build(),
-                        ContentId.newBuilder().setId(3).build()));
-        Response response =
-                mResponseBuilder.addRootFeature(ContentId.getDefaultInstance())
-                        .addClusterFeature(
-                                ContentId.getDefaultInstance(), ContentId.getDefaultInstance())
-                        .addCard(ContentId.getDefaultInstance(), ContentId.getDefaultInstance())
-                        .build();
-
-        FeedProtocolAdapter mProtocolAdapter =
-                new FeedProtocolAdapter(ImmutableList.of(mAdapter), mTimingUtils);
-        Result<Model> result = mProtocolAdapter.createModel(response);
-
-        verify(mAdapter, times(4)).determineRequiredContentIds(any(DataOperation.class));
-        assertThat(result.isSuccessful()).isTrue();
-        List<StreamDataOperation> operations = result.getValue().streamDataOperations;
-        assertThat(operations).hasSize(7);
-        assertThat(operations.get(4).getStreamStructure().getOperation())
-                .isEqualTo(StreamStructure.Operation.REQUIRED_CONTENT);
-        assertThat(operations.get(5).getStreamStructure().getOperation())
-                .isEqualTo(StreamStructure.Operation.REQUIRED_CONTENT);
-        assertThat(operations.get(6).getStreamStructure().getOperation())
-                .isEqualTo(StreamStructure.Operation.REQUIRED_CONTENT);
-        assertThat(ImmutableList.of(operations.get(4).getStreamStructure().getContentId(),
-                           operations.get(5).getStreamStructure().getContentId(),
-                           operations.get(6).getStreamStructure().getContentId()))
-                .containsExactly("::::1", "::::2", "::::3");
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/ContentDataOperationTransformerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/ContentDataOperationTransformerTest.java
deleted file mode 100644
index aab3290..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/ContentDataOperationTransformerTest.java
+++ /dev/null
@@ -1,100 +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.
-
-package org.chromium.chrome.browser.feed.library.feedprotocoladapter.internal.transformers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.ClientBasicLoggingMetadata;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.BasicLoggingMetadata;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content.Type;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation;
-import org.chromium.components.feed.core.proto.wire.FeatureProto.Feature;
-import org.chromium.components.feed.core.proto.wire.FeatureProto.Feature.RenderableUnit;
-import org.chromium.components.feed.core.proto.wire.FeedResponseProto.FeedResponseMetadata;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ContentDataOperationTransformer}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentDataOperationTransformerTest {
-    private static final String CONTENT_ID = "content-11";
-    private static final long RESPONSE_TIME = 1000;
-    private static final FeedResponseMetadata METADATA =
-            FeedResponseMetadata.newBuilder().setResponseTimeMs(RESPONSE_TIME).build();
-    private ContentDataOperationTransformer mContentDataOperationTransformer;
-    private DataOperation.Builder mDataOperation;
-    private StreamDataOperation.Builder mDataOperationBuilder;
-    private StreamFeature mStreamFeature;
-
-    @Before
-    public void setUp() {
-        mContentDataOperationTransformer = new ContentDataOperationTransformer();
-        mDataOperation = DataOperation.newBuilder();
-        mStreamFeature = StreamFeature.newBuilder().setContentId(CONTENT_ID).build();
-        mDataOperationBuilder = StreamDataOperation.newBuilder().setStreamPayload(
-                StreamPayload.newBuilder().setStreamFeature(mStreamFeature));
-    }
-
-    @Test
-    public void transform_setsContent() {
-        BasicLoggingMetadata basicLoggingMetadata =
-                BasicLoggingMetadata.newBuilder().setScore(.2f).build();
-        ClientBasicLoggingMetadata clientBasicLoggingMetadata =
-                ClientBasicLoggingMetadata.newBuilder()
-                        .setAvailabilityTimeSeconds(RESPONSE_TIME)
-                        .build();
-        Content content = Content.newBuilder()
-                                  .setType(Type.UNKNOWN_CONTENT)
-                                  .setBasicLoggingMetadata(basicLoggingMetadata)
-                                  .build();
-        mDataOperation.setFeature(Feature.newBuilder()
-                                          .setExtension(Content.contentExtension, content)
-                                          .setRenderableUnit(RenderableUnit.CONTENT));
-
-        StreamDataOperation.Builder operation = mContentDataOperationTransformer.transform(
-                mDataOperation.build(), mDataOperationBuilder, METADATA);
-
-        assertThat(operation.getStreamPayload().getStreamFeature().getContent())
-                .isEqualTo(
-                        content.toBuilder()
-                                .setBasicLoggingMetadata(
-                                        basicLoggingMetadata.toBuilder()
-                                                .setExtension(ClientBasicLoggingMetadata
-                                                                      .clientBasicLoggingMetadata,
-                                                        clientBasicLoggingMetadata)
-                                                .build())
-                                .build());
-    }
-
-    @Test
-    public void transform_responseTimeNotSet() {
-        mDataOperation.setFeature(
-                Feature.newBuilder()
-                        .setExtension(Content.contentExtension, Content.getDefaultInstance())
-                        .setRenderableUnit(RenderableUnit.CONTENT));
-        StreamDataOperation.Builder operation =
-                mContentDataOperationTransformer.transform(mDataOperation.build(),
-                        mDataOperationBuilder, FeedResponseMetadata.getDefaultInstance());
-
-        assertThat(operation).isSameInstanceAs(mDataOperationBuilder);
-    }
-
-    @Test
-    public void transform_featureIsNotContent() {
-        StreamDataOperation.Builder operation = mContentDataOperationTransformer.transform(
-                mDataOperation.build(), mDataOperationBuilder, METADATA);
-
-        assertThat(operation).isSameInstanceAs(mDataOperationBuilder);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/FeatureDataOperationTransformerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/FeatureDataOperationTransformerTest.java
deleted file mode 100644
index 912e108..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/FeatureDataOperationTransformerTest.java
+++ /dev/null
@@ -1,99 +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.
-
-package org.chromium.chrome.browser.feed.library.feedprotocoladapter.internal.transformers;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Card;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Cluster;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Stream;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation;
-import org.chromium.components.feed.core.proto.wire.FeatureProto.Feature;
-import org.chromium.components.feed.core.proto.wire.FeatureProto.Feature.RenderableUnit;
-import org.chromium.components.feed.core.proto.wire.FeedResponseProto.FeedResponseMetadata;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link FeatureDataOperationTransformer}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeatureDataOperationTransformerTest {
-    private static final String CONTENT_ID = "123";
-    private DataOperation.Builder mDataOperation;
-    private StreamDataOperation.Builder mDataOperationBuilder;
-    private StreamFeature mStreamFeature;
-    private FeatureDataOperationTransformer mFeatureDataOperationTransformer;
-
-    @Before
-    public void setUp() {
-        mFeatureDataOperationTransformer = new FeatureDataOperationTransformer();
-        mDataOperation = DataOperation.newBuilder();
-        mStreamFeature = StreamFeature.newBuilder().setContentId(CONTENT_ID).build();
-        mDataOperationBuilder = StreamDataOperation.newBuilder().setStreamPayload(
-                StreamPayload.newBuilder().setStreamFeature(mStreamFeature));
-    }
-
-    @Test
-    public void testTransformSetStream() {
-        Feature feature = Feature.newBuilder()
-                                  .setRenderableUnit(RenderableUnit.STREAM)
-                                  .setExtension(Stream.streamExtension, Stream.getDefaultInstance())
-                                  .build();
-        mDataOperation.setFeature(feature);
-        StreamFeature expectedStreamFeature =
-                mStreamFeature.toBuilder().setStream(Stream.getDefaultInstance()).build();
-
-        StreamDataOperation.Builder operation =
-                mFeatureDataOperationTransformer.transform(mDataOperation.build(),
-                        mDataOperationBuilder, FeedResponseMetadata.getDefaultInstance());
-
-        assertThat(operation.getStreamPayload().getStreamFeature())
-                .isEqualTo(expectedStreamFeature);
-    }
-
-    @Test
-    public void testTransformSetCard() {
-        Feature feature = Feature.newBuilder()
-                                  .setRenderableUnit(RenderableUnit.CARD)
-                                  .setExtension(Card.cardExtension, Card.getDefaultInstance())
-                                  .build();
-        mDataOperation.setFeature(feature);
-        StreamFeature expectedStreamFeature =
-                mStreamFeature.toBuilder().setCard(Card.getDefaultInstance()).build();
-
-        StreamDataOperation.Builder operation =
-                mFeatureDataOperationTransformer.transform(mDataOperation.build(),
-                        mDataOperationBuilder, FeedResponseMetadata.getDefaultInstance());
-
-        assertThat(operation.getStreamPayload().getStreamFeature())
-                .isEqualTo(expectedStreamFeature);
-    }
-
-    @Test
-    public void testTransformSetCluster() {
-        Feature feature =
-                Feature.newBuilder()
-                        .setRenderableUnit(RenderableUnit.CLUSTER)
-                        .setExtension(Cluster.clusterExtension, Cluster.getDefaultInstance())
-                        .build();
-        mDataOperation.setFeature(feature);
-        StreamFeature expectedStreamFeature =
-                mStreamFeature.toBuilder().setCluster(Cluster.getDefaultInstance()).build();
-
-        StreamDataOperation.Builder operation =
-                mFeatureDataOperationTransformer.transform(mDataOperation.build(),
-                        mDataOperationBuilder, FeedResponseMetadata.getDefaultInstance());
-
-        assertThat(operation.getStreamPayload().getStreamFeature())
-                .isEqualTo(expectedStreamFeature);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedActionUploadRequestManagerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedActionUploadRequestManagerTest.java
deleted file mode 100644
index 3beb6c8f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedActionUploadRequestManagerTest.java
+++ /dev/null
@@ -1,491 +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.
-
-package org.chromium.chrome.browser.feed.library.feedrequestmanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.util.Base64;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedOutputStream;
-import com.google.protobuf.ExtensionRegistryLite;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest.HttpMethod;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpResponse;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.common.testing.RequiredConsumer;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.actionmanager.FakeViewActionManager;
-import org.chromium.chrome.browser.feed.library.testing.network.FakeNetworkClient;
-import org.chromium.chrome.browser.feed.library.testing.protocoladapter.FakeProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamUploadableAction;
-import org.chromium.components.feed.core.proto.wire.ActionRequestProto.ActionRequest;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.FeedActionRequestProto.FeedActionRequest;
-import org.chromium.components.feed.core.proto.wire.FeedActionResponseProto.FeedActionResponse;
-import org.chromium.components.feed.core.proto.wire.FeedRequestProto.FeedRequest;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.components.feed.core.proto.wire.SemanticPropertiesProto.SemanticProperties;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/** Test of the {@link FeedActionUploadRequestManager} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedActionUploadRequestManagerTest {
-    public static final long ID = 42;
-    private static final ConsistencyToken TOKEN_1 =
-            ConsistencyToken.newBuilder()
-                    .setToken(ByteString.copyFrom(new byte[] {0x1, 0xa}))
-                    .build();
-    private static final ConsistencyToken TOKEN_2 =
-            ConsistencyToken.newBuilder()
-                    .setToken(ByteString.copyFrom(new byte[] {0x1, 0xf}))
-                    .build();
-    private static final ConsistencyToken TOKEN_3 =
-            ConsistencyToken.newBuilder()
-                    .setToken(ByteString.copyFrom(new byte[] {0x2, 0xa}))
-                    .build();
-    private static final String CONTENT_ID = "contentId";
-    private static final String CONTENT_ID_2 = "contentId2";
-    private static final String CONTENT_ID_LONG =
-            "extremely-long-content-id-that-should-take-a-lot-of-bytes";
-    private static final byte[] SEMANTIC_PROPERTIES_BYTES = new byte[] {0x1, 0xa};
-    private static final SemanticProperties SEMANTIC_PROPERTIES =
-            SemanticProperties.newBuilder()
-                    .setSemanticPropertiesData(ByteString.copyFrom(SEMANTIC_PROPERTIES_BYTES))
-                    .build();
-    private static final Response RESPONSE_1 =
-            Response.newBuilder()
-                    .setExtension(FeedActionResponse.feedActionResponse,
-                            FeedActionResponse.newBuilder().setConsistencyToken(TOKEN_2).build())
-                    .build();
-    private static final Response RESPONSE_2 =
-            Response.newBuilder()
-                    .setExtension(FeedActionResponse.feedActionResponse,
-                            FeedActionResponse.newBuilder().setConsistencyToken(TOKEN_3).build())
-                    .build();
-
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final FakeClock mFakeClock = new FakeClock();
-    private FakeViewActionManager mFakeViewActionManager;
-    private ExtensionRegistryLite mRegistry;
-    private FakeNetworkClient mFakeNetworkClient;
-    private FakeProtocolAdapter mFakeProtocolAdapter;
-    private FakeStore mFakeStore;
-    private FakeMainThreadRunner mFakeMainThreadRunner = FakeMainThreadRunner.runTasksImmediately();
-    private FakeTaskQueue mFakeTaskQueue;
-    private FakeThreadUtils mFakeThreadUtils;
-    private FeedActionUploadRequestManager mRequestManager;
-    private RequiredConsumer<Result<ConsistencyToken>> mConsumer;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mRegistry = ExtensionRegistryLite.newInstance();
-        mRegistry.add(FeedRequest.feedRequest);
-        mRegistry.add(FeedActionRequest.feedActionRequest);
-        mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-        mFakeNetworkClient = new FakeNetworkClient(mFakeThreadUtils);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeProtocolAdapter = new FakeProtocolAdapter();
-        mFakeStore = new FakeStore(mConfiguration, mFakeThreadUtils, mFakeTaskQueue, mFakeClock);
-        mFakeViewActionManager = new FakeViewActionManager(mFakeStore);
-        mConsumer = new RequiredConsumer<>(input -> { mFakeThreadUtils.checkNotMainThread(); });
-
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", VERSION_CODES.KITKAT);
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "RELEASE", "4.4.3");
-        ReflectionHelpers.setStaticField(Build.class, "CPU_ABI", "armeabi");
-        ReflectionHelpers.setStaticField(Build.class, "TAGS", "dev-keys");
-        mRequestManager = createRequestManager(mConfiguration);
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeTaskQueue.initialize(() -> {});
-    }
-
-    @Test
-    public void testTriggerUploadActions_ttlExceededRemove() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 5L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        StreamUploadableAction action = StreamUploadableAction.newBuilder()
-                                                .setUploadAttempts(1)
-                                                .setTimestampSeconds(1L)
-                                                .setFeatureContentId(CONTENT_ID)
-                                                .build();
-        mFakeStore.setStreamUploadableActions(action);
-        mFakeClock.set(5000L);
-        mFakeNetworkClient.addResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mRequestManager.triggerUploadActions(
-                setOf(action), ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mFakeStore.getContentById(CONTENT_ID)).isEmpty();
-    }
-
-    @Test
-    public void testTriggerUploadActions_maxUploadsRemove() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        StreamUploadableAction action = StreamUploadableAction.newBuilder()
-                                                .setUploadAttempts(2)
-                                                .setFeatureContentId(CONTENT_ID)
-                                                .build();
-        mFakeNetworkClient.addResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mRequestManager.triggerUploadActions(
-                setOf(action), ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mFakeStore.getContentById(CONTENT_ID)).isEmpty();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchSuccess() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 1L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_2).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isTrue();
-            assertThat(input.getValue().toByteArray()).isEqualTo(TOKEN_3.toByteArray());
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_1))
-                .addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_2));
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchFirstFailure() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_2).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isFalse();
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 500, RESPONSE_1));
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchFirstSuccessSecondFailure() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_2).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isTrue();
-            assertThat(input.getValue().toByteArray()).isEqualTo(TOKEN_2.toByteArray());
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_1))
-                .addResponse(createHttpResponse(/* responseCode= */ 500, RESPONSE_2));
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchFirstReachesMaxNumActions() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 1L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_2).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isTrue();
-            assertThat(input.getValue().toByteArray()).isEqualTo(TOKEN_2.toByteArray());
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_1));
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchFirstReachesMaxSize() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 1L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        Set<StreamUploadableAction> actionSet = orderedSetOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_LONG).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isTrue();
-            assertThat(input.getValue().toByteArray()).isEqualTo(TOKEN_2.toByteArray());
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_1));
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_batchNoUploadableActions() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 1L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_LONG).build());
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isFalse();
-        });
-        mRequestManager.triggerUploadActions(actionSet, TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_getMethod() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-        StreamUploadableAction action =
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build();
-        Set<StreamUploadableAction> actionSet = setOf(action);
-        mFakeNetworkClient.addResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mRequestManager.triggerUploadActions(
-                actionSet, ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest);
-
-        ActionRequest request = getActionRequestFromHttpRequest(httpRequest);
-        UploadableActionsRequestBuilder builder =
-                new UploadableActionsRequestBuilder(mFakeProtocolAdapter);
-        ActionRequest expectedRequest =
-                builder.setConsistencyToken(ConsistencyToken.getDefaultInstance())
-                        .setActions(actionSet)
-                        .build();
-        assertThat(request.toByteArray()).isEqualTo(expectedRequest.toByteArray());
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mFakeStore.getContentById(CONTENT_ID))
-                .contains(StreamUploadableAction.newBuilder()
-                                  .setFeatureContentId(CONTENT_ID)
-                                  .setUploadAttempts(1)
-                                  .build());
-    }
-
-    @Test
-    public void testTriggerUploadActions_defaultMethod() throws Exception {
-        Set<StreamUploadableAction> actionSet =
-                setOf(StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build());
-        mFakeNetworkClient.addResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mRequestManager.triggerUploadActions(
-                actionSet, ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-
-        ActionRequest request = getActionRequestFromHttpRequestBody(httpRequest);
-        UploadableActionsRequestBuilder builder =
-                new UploadableActionsRequestBuilder(mFakeProtocolAdapter);
-        Set<StreamUploadableAction> expectedActionSet =
-                setOf(StreamUploadableAction.newBuilder()
-                                .setFeatureContentId(CONTENT_ID)
-                                .setUploadAttempts(1)
-                                .build());
-        ActionRequest expectedRequest =
-                builder.setConsistencyToken(ConsistencyToken.getDefaultInstance())
-                        .setActions(expectedActionSet)
-                        .build();
-        assertThat(request.toByteArray()).isEqualTo(expectedRequest.toByteArray());
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions_withSemanticProperties() throws Exception {
-        mFakeStore.addSemanticProperties(CONTENT_ID, SEMANTIC_PROPERTIES);
-        Set<StreamUploadableAction> actionSet =
-                setOf(StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build());
-        mFakeNetworkClient.addResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mRequestManager.triggerUploadActions(
-                actionSet, ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-
-        ActionRequest request = getActionRequestFromHttpRequestBody(httpRequest);
-        assertThat(request.getExtension(FeedActionRequest.feedActionRequest)
-                           .getFeedActionList()
-                           .get(0)
-                           .getSemanticProperties()
-                           .getSemanticPropertiesData())
-                .isEqualTo(SEMANTIC_PROPERTIES.getSemanticPropertiesData());
-    }
-
-    @Test
-    public void testTriggerUploadAllActions() throws Exception {
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_ACTION_SERVER_METHOD, HttpMethod.GET)
-                        .put(ConfigKey.FEED_ACTION_TTL_SECONDS, 1000L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_SIZE_PER_REQUEST, 20L)
-                        .put(ConfigKey.FEED_ACTION_MAX_UPLOAD_ATTEMPTS, 1L)
-                        .put(ConfigKey.FEED_ACTION_SERVER_MAX_ACTIONS_PER_REQUEST, 2L)
-                        .build();
-        mRequestManager = createRequestManager(configuration);
-
-        Set<StreamUploadableAction> actionSet = setOf(
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID_2).build());
-        mFakeViewActionManager.mViewActions.addAll(actionSet);
-        mConsumer = new RequiredConsumer<>(input -> {
-            mFakeThreadUtils.checkNotMainThread();
-            assertThat(input.isSuccessful()).isTrue();
-            assertThat(input.getValue().toByteArray()).isEqualTo(TOKEN_3.toByteArray());
-        });
-        mFakeNetworkClient.addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_1))
-                .addResponse(createHttpResponse(/* responseCode= */ 200, RESPONSE_2));
-        mRequestManager.triggerUploadAllActions(TOKEN_1, mConsumer);
-
-        assertThat(mConsumer.isCalled()).isTrue();
-    }
-
-    private static void assertHttpRequestFormattedCorrectly(HttpRequest httpRequest) {
-        assertThat(httpRequest.getBody()).hasLength(0);
-        assertThat(httpRequest.getMethod()).isEqualTo(HttpMethod.GET);
-        assertThat(httpRequest.getUri().getQueryParameter("fmt")).isEqualTo("bin");
-        assertThat(httpRequest.getUri().getQueryParameter(RequestHelper.MOTHERSHIP_PARAM_PAYLOAD))
-                .isNotNull();
-    }
-
-    private static HttpResponse createHttpResponse(int responseCode, Response response)
-            throws IOException {
-        byte[] rawResponse = response.toByteArray();
-        ByteBuffer buffer = ByteBuffer.allocate(rawResponse.length + (Integer.SIZE / 8));
-        CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(buffer);
-        codedOutputStream.writeUInt32NoTag(rawResponse.length);
-        codedOutputStream.writeRawBytes(rawResponse);
-        codedOutputStream.flush();
-        return new HttpResponse(responseCode, buffer.array(), false);
-    }
-
-    private ActionRequest getActionRequestFromHttpRequest(HttpRequest httpRequest)
-            throws Exception {
-        return ActionRequest.parseFrom(
-                Base64.decode(httpRequest.getUri().getQueryParameter(
-                                      RequestHelper.MOTHERSHIP_PARAM_PAYLOAD),
-                        Base64.URL_SAFE),
-                mRegistry);
-    }
-
-    private ActionRequest getActionRequestFromHttpRequestBody(HttpRequest httpRequest)
-            throws Exception {
-        return ActionRequest.parseFrom(httpRequest.getBody(), mRegistry);
-    }
-
-    private FeedActionUploadRequestManager createRequestManager(Configuration configuration) {
-        return new FeedActionUploadRequestManager(mFakeViewActionManager, configuration,
-                mFakeNetworkClient, mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new),
-                mFakeMainThreadRunner, mFakeTaskQueue, mFakeThreadUtils, mFakeStore, mFakeClock);
-    }
-
-    private static <T> Set<T> setOf(T... items) {
-        Set<T> result = new HashSet<>();
-        Collections.addAll(result, items);
-        return result;
-    }
-
-    private static <T> Set<T> orderedSetOf(T... items) {
-        Set<T> result = new LinkedHashSet<>();
-        Collections.addAll(result, items);
-        return result;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedRequestManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedRequestManagerImplTest.java
deleted file mode 100644
index 040507d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedRequestManagerImplTest.java
+++ /dev/null
@@ -1,1219 +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.
-
-package org.chromium.chrome.browser.feed.library.feedrequestmanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.util.Base64;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedOutputStream;
-import com.google.protobuf.ExtensionRegistryLite;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.base.test.util.MetricsUtils;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest.HttpMethod;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpResponse;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipInfo.FeatureName;
-import org.chromium.chrome.browser.feed.library.api.internal.common.DismissActionWithSemanticProperties;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.locale.LocaleUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.common.testing.RequiredConsumer;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.actionmanager.FakeActionReader;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.testing.host.stream.FakeTooltipSupportedApi;
-import org.chromium.chrome.browser.feed.library.testing.network.FakeNetworkClient;
-import org.chromium.chrome.browser.feed.library.testing.protocoladapter.FakeProtocolAdapter;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProviderJni;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.wire.ActionTypeProto.ActionType;
-import org.chromium.components.feed.core.proto.wire.CapabilityProto.Capability;
-import org.chromium.components.feed.core.proto.wire.ClientInfoProto.ClientInfo;
-import org.chromium.components.feed.core.proto.wire.ClientInfoProto.ClientInfo.AppType;
-import org.chromium.components.feed.core.proto.wire.ClientInfoProto.ClientInfo.PlatformType;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.DisplayInfoProto.DisplayInfo;
-import org.chromium.components.feed.core.proto.wire.FeedActionQueryDataProto.Action;
-import org.chromium.components.feed.core.proto.wire.FeedActionQueryDataProto.FeedActionQueryData;
-import org.chromium.components.feed.core.proto.wire.FeedActionQueryDataProto.FeedActionQueryDataItem;
-import org.chromium.components.feed.core.proto.wire.FeedQueryProto.FeedQuery;
-import org.chromium.components.feed.core.proto.wire.FeedRequestProto.FeedRequest;
-import org.chromium.components.feed.core.proto.wire.FeedResponseProto.FeedResponse;
-import org.chromium.components.feed.core.proto.wire.RequestProto.Request;
-import org.chromium.components.feed.core.proto.wire.RequestProto.Request.RequestVersion;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.components.feed.core.proto.wire.SemanticPropertiesProto.SemanticProperties;
-import org.chromium.components.feed.core.proto.wire.VersionProto.Version;
-import org.chromium.components.feed.core.proto.wire.VersionProto.Version.Architecture;
-import org.chromium.components.feed.core.proto.wire.VersionProto.Version.BuildType;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.components.user_prefs.UserPrefsJni;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-/** Test of the {@link FeedRequestManagerImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
-@Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)
-@Features.
-DisableFeatures({ChromeFeatureList.REPORT_FEED_USER_ACTIONS, ChromeFeatureList.INTEREST_FEED_V2,
-        ChromeFeatureList.INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS})
-public class FeedRequestManagerImplTest {
-    private static final int NOT_FOUND = 404;
-    private static final String TABLE = "table";
-    private static final String TABLE_2 = "table2";
-    private static final String CONTENT_DOMAIN = "contentDomain";
-    private static final String CONTENT_DOMAIN_2 = "contentDomain2";
-    public static final long ID = 42;
-    private static final long ID_2 = 2;
-    private static final String APP_VERSION_STRING = "5.7";
-
-    private final FakeClock mFakeClock = new FakeClock();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    private SchedulerApi mScheduler;
-    @Mock
-    private ApplicationInfo mApplicationInfo;
-    @Mock
-    private IdentityServicesProvider.Natives mIdentityServicesProviderJniMock;
-    @Mock
-    private Profile mProfileMock;
-    @Mock
-    private IdentityManager mIdentifiyManagerMock;
-    @Mock
-    private UserPrefs.Natives mUserPrefsJniMock;
-    @Mock
-    private Profile mProfile;
-    @Mock
-    private PrefService mPrefService;
-
-    private Context mContext;
-    private ExtensionRegistryLite mRegistry;
-    private FeedRequestManagerImpl mRequestManager;
-    private FakeActionReader mFakeActionReader;
-    private FakeMainThreadRunner mFakeMainThreadRunner;
-    private FakeProtocolAdapter mFakeProtocolAdapter;
-    private FakeThreadUtils mFakeThreadUtils;
-    private FakeTaskQueue mFakeTaskQueue;
-    private FakeBasicLoggingApi mFakeBasicLoggingApi;
-    private FakeNetworkClient mFakeNetworkClient;
-    private FakeTooltipSupportedApi mFakeTooltipSupportedApi;
-    private RequiredConsumer<Result<Model>> mConsumer;
-    private Result<Model> mConsumedResult = Result.failure();
-    private HttpResponse mFailingResponse;
-
-    @Rule
-    public JniMocker jniMocker = new JniMocker();
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mContext.getResources().getConfiguration().locale = Locale.US;
-        FeedExtensionRegistry feedExtensionRegistry = new FeedExtensionRegistry(ArrayList::new);
-        mRegistry = ExtensionRegistryLite.newInstance();
-        mRegistry.add(FeedRequest.feedRequest);
-        mFakeActionReader = new FakeActionReader();
-        mFakeProtocolAdapter = new FakeProtocolAdapter();
-        mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-        mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-        mFakeMainThreadRunner =
-                FakeMainThreadRunner.runTasksImmediatelyWithThreadChecks(mFakeThreadUtils);
-        mFakeNetworkClient = new FakeNetworkClient(mFakeThreadUtils);
-        mFakeNetworkClient.setDefaultResponse(
-                createHttpResponse(/* responseCode= */ 200, Response.getDefaultInstance()));
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeTaskQueue.initialize(() -> {});
-        mFakeTooltipSupportedApi = new FakeTooltipSupportedApi(mFakeThreadUtils);
-        mFailingResponse =
-                createHttpResponse(/* responseCode= */ NOT_FOUND, Response.getDefaultInstance());
-        mConsumer = new RequiredConsumer<>(input -> { mConsumedResult = input; });
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", VERSION_CODES.KITKAT);
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "RELEASE", "4.4.3");
-        ReflectionHelpers.setStaticField(Build.class, "CPU_ABI", "armeabi");
-        ReflectionHelpers.setStaticField(Build.class, "TAGS", "dev-keys");
-        when(mApplicationInfo.getAppType()).thenReturn(ApplicationInfo.AppType.CHROME);
-        when(mApplicationInfo.getArchitecture()).thenReturn(ApplicationInfo.Architecture.ARM);
-        when(mApplicationInfo.getBuildType()).thenReturn(ApplicationInfo.BuildType.DEV);
-        when(mApplicationInfo.getVersionString()).thenReturn(APP_VERSION_STRING);
-
-        Profile.setLastUsedProfileForTesting(mProfileMock);
-        jniMocker.mock(IdentityServicesProviderJni.TEST_HOOKS, mIdentityServicesProviderJniMock);
-        when(mIdentityServicesProviderJniMock.getIdentityManager(mProfileMock))
-                .thenReturn(mIdentifiyManagerMock);
-
-        jniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
-        when(mUserPrefsJniMock.get(mProfileMock)).thenReturn(mPrefService);
-
-        mRequestManager = new FeedRequestManagerImpl(mConfiguration, mFakeNetworkClient,
-                mFakeProtocolAdapter, feedExtensionRegistry, mScheduler, mFakeTaskQueue,
-                mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext, mApplicationInfo,
-                mFakeMainThreadRunner, mFakeBasicLoggingApi, mFakeTooltipSupportedApi);
-    }
-
-    @After
-    public void tearDown() {
-        Profile.setLastUsedProfileForTesting(null);
-    }
-
-    @Test
-    public void testTriggerRefresh() throws Exception {
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        assertThat(mFakeTooltipSupportedApi.getLatestFeatureName()).isNull();
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-        assertThat(httpRequest.getUri().getQueryParameter(RequestHelper.PRIORITY_PARAM))
-                .isEqualTo(RequestHelper.PRIORITY_VALUE_BACKGROUND);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testTriggerRefresh_setNoticeCardPrefAndRecordBothHistograms() throws Exception {
-        MetricsUtils.HistogramDelta obsoleteNoticeCardNotFulfilledDelta =
-                new MetricsUtils.HistogramDelta(
-                        "ContentSuggestions.Feed.NoticeCardFulfilled", 0 /*false*/);
-        MetricsUtils.HistogramDelta obsoleteNoticeCardFulfilledDelta =
-                new MetricsUtils.HistogramDelta(
-                        "ContentSuggestions.Feed.NoticeCardFulfilled", 1 /*true*/);
-        MetricsUtils.HistogramDelta noticeCardNotFulfilledDelta = new MetricsUtils.HistogramDelta(
-                "ContentSuggestions.Feed.NoticeCardFulfilled2", 0 /*false*/);
-        MetricsUtils.HistogramDelta noticeCardFulfilledDelta = new MetricsUtils.HistogramDelta(
-                "ContentSuggestions.Feed.NoticeCardFulfilled2", 1 /*true*/);
-
-        // Skip the read of the int that determines the length of the encoded proto. This is to
-        // avoid having to encode the length which is a feature we don't want to test here.
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_SERVER_RESPONSE_LENGTH_PREFIXED, false)
-                        .build();
-
-        mRequestManager = new FeedRequestManagerImpl(configuration, mFakeNetworkClient,
-                mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new), mScheduler,
-                mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext,
-                mApplicationInfo, mFakeMainThreadRunner, mFakeBasicLoggingApi,
-                mFakeTooltipSupportedApi);
-
-        // Trigger a refresh that has a notice card.
-        Response response =
-                Response.newBuilder()
-                        .setExtension(FeedResponse.feedResponse,
-                                FeedResponse.newBuilder()
-                                        .addServerCapabilities(
-                                                Capability.REPORT_FEED_USER_ACTIONS_NOTICE_CARD)
-                                        .build())
-                        .build();
-        mFakeNetworkClient.addResponse(new HttpResponse(200, response.toByteArray(), false));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-        verify(mPrefService, times(1)).setBoolean(Pref.LAST_FETCH_HAD_NOTICE_CARD, true);
-        assertThat(noticeCardNotFulfilledDelta.getDelta()).isEqualTo(0);
-        assertThat(noticeCardFulfilledDelta.getDelta()).isEqualTo(1);
-
-        // Trigger a refresh that doesn't have a notice card.
-        mFakeNetworkClient.addResponse(
-                new HttpResponse(200, Response.getDefaultInstance().toByteArray(), false));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-        assertThat(obsoleteNoticeCardNotFulfilledDelta.getDelta()).isEqualTo(1);
-        assertThat(obsoleteNoticeCardFulfilledDelta.getDelta()).isEqualTo(1);
-        assertThat(noticeCardNotFulfilledDelta.getDelta()).isEqualTo(1);
-        assertThat(noticeCardFulfilledDelta.getDelta()).isEqualTo(1);
-    }
-
-    @Test
-    public void testTriggerRefresh_setLastRefreshWasSignedInPref() throws Exception {
-        // Skip the read of the int that determines the length of the encoded proto. This is to
-        // avoid having to encode the length which is a feature we don't want to test here.
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_SERVER_RESPONSE_LENGTH_PREFIXED, false)
-                        .build();
-
-        mRequestManager = new FeedRequestManagerImpl(configuration, mFakeNetworkClient,
-                mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new), mScheduler,
-                mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext,
-                mApplicationInfo, mFakeMainThreadRunner, mFakeBasicLoggingApi,
-                mFakeTooltipSupportedApi);
-
-        mFakeNetworkClient.addResponse(
-                new HttpResponse(200, Response.getDefaultInstance().toByteArray(), true));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        verify(mPrefService, times(1)).setBoolean(Pref.LAST_REFRESH_WAS_SIGNED_IN, true);
-    }
-
-    @Test
-    public void testLoadMore_dontSetNoticeCardPrefAndOnlyRecordObsoleteHistogram()
-            throws Exception {
-        MetricsUtils.HistogramDelta obsoleteNoticeCardNotFulfilledDelta =
-                new MetricsUtils.HistogramDelta(
-                        "ContentSuggestions.Feed.NoticeCardFulfilled", 0 /*false*/);
-        MetricsUtils.HistogramDelta obsoleteNoticeCardFulfilledDelta =
-                new MetricsUtils.HistogramDelta(
-                        "ContentSuggestions.Feed.NoticeCardFulfilled", 1 /*true*/);
-        MetricsUtils.HistogramDelta noticeCardNotFulfilledDelta = new MetricsUtils.HistogramDelta(
-                "ContentSuggestions.Feed.NoticeCardFulfilled2", 0 /*false*/);
-        MetricsUtils.HistogramDelta noticeCardFulfilledDelta = new MetricsUtils.HistogramDelta(
-                "ContentSuggestions.Feed.NoticeCardFulfilled2", 1 /*true*/);
-
-        // Skip the read of the int that determines the length of the encoded proto. This is to
-        // avoid having to encode the length which is a feature we don't want to test here.
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_SERVER_RESPONSE_LENGTH_PREFIXED, false)
-                        .build();
-
-        mRequestManager = new FeedRequestManagerImpl(configuration, mFakeNetworkClient,
-                mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new), mScheduler,
-                mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext,
-                mApplicationInfo, mFakeMainThreadRunner, mFakeBasicLoggingApi,
-                mFakeTooltipSupportedApi);
-
-        mFakeThreadUtils.enforceMainThread(false);
-
-        // Trigger a load more with a notice card in the query response.
-        Response response =
-                Response.newBuilder()
-                        .setExtension(FeedResponse.feedResponse,
-                                FeedResponse.newBuilder()
-                                        .addServerCapabilities(
-                                                Capability.REPORT_FEED_USER_ACTIONS_NOTICE_CARD)
-                                        .build())
-                        .build();
-        mFakeNetworkClient.addResponse(new HttpResponse(200, response.toByteArray(), false));
-        StreamToken token =
-                StreamToken.newBuilder()
-                        .setNextPageToken(ByteString.copyFrom("abc", Charset.defaultCharset()))
-                        .build();
-        mRequestManager.loadMore(token, ConsistencyToken.getDefaultInstance(), input -> {});
-
-        // Trigger a load more without a notice card in the query response.
-        mFakeNetworkClient.addResponse(
-                new HttpResponse(200, Response.getDefaultInstance().toByteArray(), false));
-        mRequestManager.loadMore(token, ConsistencyToken.getDefaultInstance(), input -> {});
-
-        // Verify that only the obsolete histograms were recorded.
-        assertThat(noticeCardNotFulfilledDelta.getDelta()).isEqualTo(0);
-        assertThat(noticeCardFulfilledDelta.getDelta()).isEqualTo(0);
-        assertThat(obsoleteNoticeCardNotFulfilledDelta.getDelta()).isEqualTo(1);
-        assertThat(obsoleteNoticeCardFulfilledDelta.getDelta()).isEqualTo(1);
-        // Verify that no attempts were made to update the notice card presence pref.
-        verify(mPrefService, never()).setBoolean(eq(Pref.LAST_FETCH_HAD_NOTICE_CARD), anyBoolean());
-    }
-
-    @Test
-    public void testLoadMore_dontSetLastRefreshWasSignedInPref() throws Exception {
-        // Skip the read of the int that determines the length of the encoded proto. This is to
-        // avoid having to encode the length which is a feature we don't want to test here.
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.FEED_SERVER_RESPONSE_LENGTH_PREFIXED, false)
-                        .build();
-
-        mRequestManager = new FeedRequestManagerImpl(configuration, mFakeNetworkClient,
-                mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new), mScheduler,
-                mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext,
-                mApplicationInfo, mFakeMainThreadRunner, mFakeBasicLoggingApi,
-                mFakeTooltipSupportedApi);
-
-        mFakeNetworkClient.addResponse(
-                new HttpResponse(200, Response.getDefaultInstance().toByteArray(), false));
-        StreamToken token =
-                StreamToken.newBuilder()
-                        .setNextPageToken(ByteString.copyFrom("abc", Charset.defaultCharset()))
-                        .build();
-        mFakeThreadUtils.enforceMainThread(false);
-        mRequestManager.loadMore(token, ConsistencyToken.getDefaultInstance(), input -> {});
-
-        verify(mPrefService, never()).setBoolean(Pref.LAST_REFRESH_WAS_SIGNED_IN, true);
-    }
-
-    @Test
-    public void testTriggerRefresh_FeedUiCapabilityAddedWhenFlagIsOn() throws Exception {
-        testCapabilityAdded(ConfigKey.FEED_UI_ENABLED, Capability.FEED_UI);
-    }
-
-    @Test
-    public void testTriggerRefresh_undoableActionCapabilityAddedWhenFlagIsOn() throws Exception {
-        testCapabilityAdded(ConfigKey.UNDOABLE_ACTIONS_ENABLED, Capability.UNDOABLE_ACTIONS);
-    }
-
-    @Test
-    public void testTriggerRefresh_manageInterestsCapabilityAddedWhenFlagIsOn() throws Exception {
-        testCapabilityAdded(ConfigKey.MANAGE_INTERESTS_ENABLED, Capability.MANAGE_INTERESTS);
-    }
-
-    @Test
-    public void testTriggerRefresh_sendFeedbackCapabilityAdded() throws Exception {
-        testCapabilityAdded(Capability.SEND_FEEDBACK);
-    }
-
-    @Test
-    public void testTriggerRefresh_tooltipCapabilityAddedWhenFlagIsOn() throws Exception {
-        testCapabilityAdded(ConfigKey.CARD_MENU_TOOLTIP_ELIGIBLE, Capability.CARD_MENU_TOOLTIP);
-    }
-
-    @Test
-    public void testTriggerRefresh_tooltipCapabilityNotAdded() throws Exception {
-        // If the config key for card menu tool tip is set but the
-        // TooltipSupportedApi.wouldTriggerHelpUi() returns false, then the capability should not be
-        // added and only the BASE_UI should be present.
-        mFakeTooltipSupportedApi.addUnsupportedFeature(FeatureName.CARD_MENU_TOOLTIP);
-        testCapabilityAdded(ConfigKey.CARD_MENU_TOOLTIP_ELIGIBLE /* capability= empty */);
-    }
-
-    @Test
-    public void testTriggerRefresh_useSecondaryPageRequestAdded() throws Exception {
-        testCapabilityAdded(
-                ConfigKey.USE_SECONDARY_PAGE_REQUEST, Capability.USE_SECONDARY_PAGE_REQUEST);
-    }
-
-    @Test
-    public void testTriggerRefresh_articleSnippetsAdded() throws Exception {
-        testCapabilityAdded(ConfigKey.SNIPPETS_ENABLED, Capability.ARTICLE_SNIPPETS);
-    }
-
-    @Test
-    public void testTriggerRefresh_enableCarouselsAdded() throws Exception {
-        testCapabilityAdded(ConfigKey.ENABLE_CAROUSELS, Capability.CAROUSELS);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.REPORT_FEED_USER_ACTIONS)
-    public void testTriggerRefresh_enableFeedActions() throws Exception {
-        testCapabilityAdded(Capability.CLICK_ACTION, Capability.VIEW_ACTION,
-                Capability.REPORT_FEED_USER_ACTIONS_NOTICE_CARD);
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS})
-    public void testTriggerRefresh_acknowledgeNoticeCard_whenClicksThresholdReached()
-            throws Exception {
-        // Simulate enough clicks.
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_CLICKS_COUNT)).thenReturn(1);
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_VIEWS_COUNT)).thenReturn(0);
-
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        assertTrue(getRequestFromHttpRequest(httpRequest)
-                           .getExtension(FeedRequest.feedRequest)
-                           .getFeedQuery()
-                           .getChromeFulfillmentInfo()
-                           .getNoticeCardAcknowledged());
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS})
-    public void testTriggerRefresh_acknowledgeNoticeCard_whenViewsThresholdReached()
-            throws Exception {
-        // Simulate enough views.
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_CLICKS_COUNT)).thenReturn(0);
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_VIEWS_COUNT)).thenReturn(3);
-
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        assertTrue(getRequestFromHttpRequest(httpRequest)
-                           .getExtension(FeedRequest.feedRequest)
-                           .getFeedQuery()
-                           .getChromeFulfillmentInfo()
-                           .getNoticeCardAcknowledged());
-    }
-
-    @Test
-    public void testTriggerRefresh_dontAcknowledgeNoticeCard_whenFeatureDisabled()
-            throws Exception {
-        // Simulate enough views and clicks.
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_CLICKS_COUNT)).thenReturn(1);
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_VIEWS_COUNT)).thenReturn(3);
-
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        assertFalse(getRequestFromHttpRequest(httpRequest)
-                            .getExtension(FeedRequest.feedRequest)
-                            .getFeedQuery()
-                            .getChromeFulfillmentInfo()
-                            .getNoticeCardAcknowledged());
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS})
-    public void testTriggerRefresh_dontAcknowledgeNoticeCard_whenCountThresholdsNotReached()
-            throws Exception {
-        // Simulate not enough views nor clicks.
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_CLICKS_COUNT)).thenReturn(0);
-        when(mPrefService.getInteger(Pref.NOTICE_CARD_VIEWS_COUNT)).thenReturn(2);
-
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        assertFalse(getRequestFromHttpRequest(httpRequest)
-                            .getExtension(FeedRequest.feedRequest)
-                            .getFeedQuery()
-                            .getChromeFulfillmentInfo()
-                            .getNoticeCardAcknowledged());
-    }
-
-    @Test
-    public void testActionData_simpleDismiss() throws Exception {
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, null));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Collections.singletonList(ID);
-        List<String> expectedContentDomains = Collections.singletonList(CONTENT_DOMAIN);
-        List<String> expectedTables = Collections.singletonList(TABLE);
-        List<SemanticProperties> expectedSemanticProperties = Collections.emptyList();
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Collections.singletonList(FeedActionQueryDataItem.newBuilder()
-                                                  .setIdIndex(0)
-                                                  .setContentDomainIndex(0)
-                                                  .setTableIndex(0)
-                                                  .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_uniqueDismisses() throws Exception {
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, null),
-                buildDismissAction(ID_2, CONTENT_DOMAIN_2, TABLE_2, null));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Arrays.asList(ID, ID_2);
-        List<String> expectedContentDomains = Arrays.asList(CONTENT_DOMAIN, CONTENT_DOMAIN_2);
-        List<String> expectedTables = Arrays.asList(TABLE, TABLE_2);
-        List<SemanticProperties> expectedSemanticProperties = Collections.emptyList();
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Arrays.asList(FeedActionQueryDataItem.newBuilder()
-                                      .setIdIndex(0)
-                                      .setContentDomainIndex(0)
-                                      .setTableIndex(0)
-                                      .build(),
-                        FeedActionQueryDataItem.newBuilder()
-                                .setIdIndex(1)
-                                .setContentDomainIndex(1)
-                                .setTableIndex(1)
-                                .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_overlappingDismisses() throws Exception {
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, null),
-                buildDismissAction(ID_2, CONTENT_DOMAIN, TABLE, null));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Arrays.asList(ID, ID_2);
-        List<String> expectedContentDomains = Collections.singletonList(CONTENT_DOMAIN);
-        List<String> expectedTables = Collections.singletonList(TABLE);
-        List<SemanticProperties> expectedSemanticProperties = Collections.emptyList();
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Arrays.asList(FeedActionQueryDataItem.newBuilder()
-                                      .setIdIndex(0)
-                                      .setContentDomainIndex(0)
-                                      .setTableIndex(0)
-                                      .build(),
-                        FeedActionQueryDataItem.newBuilder()
-                                .setIdIndex(1)
-                                .setContentDomainIndex(0)
-                                .setTableIndex(0)
-                                .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_simpleDismissWithSemanticProperties() throws Exception {
-        byte[] semanticPropertiesBytes = {42, 17, 88};
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, semanticPropertiesBytes));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Collections.singletonList(ID);
-        List<String> expectedContentDomains = Collections.singletonList(CONTENT_DOMAIN);
-        List<String> expectedTables = Collections.singletonList(TABLE);
-        List<SemanticProperties> expectedSemanticProperties = Collections.singletonList(
-                SemanticProperties.newBuilder()
-                        .setSemanticPropertiesData(ByteString.copyFrom(semanticPropertiesBytes))
-                        .build());
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Collections.singletonList(FeedActionQueryDataItem.newBuilder()
-                                                  .setIdIndex(0)
-                                                  .setContentDomainIndex(0)
-                                                  .setTableIndex(0)
-                                                  .setSemanticPropertiesIndex(0)
-                                                  .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_uniqueDismissesWithSemanticProperties() throws Exception {
-        byte[] semanticPropertiesBytes = {42, 17, 88};
-        byte[] semanticPropertiesBytes2 = {7, 43, 91};
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, semanticPropertiesBytes),
-                buildDismissAction(ID_2, CONTENT_DOMAIN_2, TABLE_2, semanticPropertiesBytes2));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Arrays.asList(ID, ID_2);
-        List<String> expectedContentDomains = Arrays.asList(CONTENT_DOMAIN, CONTENT_DOMAIN_2);
-        List<String> expectedTables = Arrays.asList(TABLE, TABLE_2);
-        List<SemanticProperties> expectedSemanticProperties = Arrays.asList(
-                SemanticProperties.newBuilder()
-                        .setSemanticPropertiesData(ByteString.copyFrom(semanticPropertiesBytes))
-                        .build(),
-                SemanticProperties.newBuilder()
-                        .setSemanticPropertiesData(ByteString.copyFrom(semanticPropertiesBytes2))
-                        .build());
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Arrays.asList(FeedActionQueryDataItem.newBuilder()
-                                      .setIdIndex(0)
-                                      .setContentDomainIndex(0)
-                                      .setTableIndex(0)
-                                      .setSemanticPropertiesIndex(0)
-                                      .build(),
-                        FeedActionQueryDataItem.newBuilder()
-                                .setIdIndex(1)
-                                .setContentDomainIndex(1)
-                                .setTableIndex(1)
-                                .setSemanticPropertiesIndex(1)
-                                .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_overlappingDismissesWithSemanticProperties() throws Exception {
-        byte[] semanticPropertiesBytes = {42, 17, 88};
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, semanticPropertiesBytes),
-                buildDismissAction(ID_2, CONTENT_DOMAIN, TABLE_2, semanticPropertiesBytes));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Arrays.asList(ID, ID_2);
-        List<String> expectedContentDomains = Collections.singletonList(CONTENT_DOMAIN);
-        List<String> expectedTables = Arrays.asList(TABLE, TABLE_2);
-        List<SemanticProperties> expectedSemanticProperties = Collections.singletonList(
-                SemanticProperties.newBuilder()
-                        .setSemanticPropertiesData(ByteString.copyFrom(semanticPropertiesBytes))
-                        .build());
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Arrays.asList(FeedActionQueryDataItem.newBuilder()
-                                      .setIdIndex(0)
-                                      .setContentDomainIndex(0)
-                                      .setTableIndex(0)
-                                      .setSemanticPropertiesIndex(0)
-                                      .build(),
-                        FeedActionQueryDataItem.newBuilder()
-                                .setIdIndex(1)
-                                .setContentDomainIndex(0)
-                                .setTableIndex(1)
-                                .setSemanticPropertiesIndex(0)
-                                .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testActionData_someDismissesWithSemanticProperties() throws Exception {
-        byte[] semanticPropertiesBytes = {42, 17, 88};
-        mFakeActionReader.addDismissActionsWithSemanticProperties(
-                buildDismissAction(ID, CONTENT_DOMAIN, TABLE, null),
-                buildDismissAction(ID_2, CONTENT_DOMAIN_2, TABLE_2, semanticPropertiesBytes));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-
-        List<Long> expectedIds = Arrays.asList(ID, ID_2);
-        List<String> expectedContentDomains = Arrays.asList(CONTENT_DOMAIN, CONTENT_DOMAIN_2);
-        List<String> expectedTables = Arrays.asList(TABLE, TABLE_2);
-        List<SemanticProperties> expectedSemanticProperties = Collections.singletonList(
-                SemanticProperties.newBuilder()
-                        .setSemanticPropertiesData(ByteString.copyFrom(semanticPropertiesBytes))
-                        .build());
-        List<FeedActionQueryDataItem> expectedDataItems =
-                Arrays.asList(FeedActionQueryDataItem.newBuilder()
-                                      .setIdIndex(0)
-                                      .setContentDomainIndex(0)
-                                      .setTableIndex(0)
-                                      .build(),
-                        FeedActionQueryDataItem.newBuilder()
-                                .setIdIndex(1)
-                                .setContentDomainIndex(1)
-                                .setTableIndex(1)
-                                .setSemanticPropertiesIndex(0)
-                                .build());
-
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .addFeedActionQueryData(
-                                                FeedActionQueryData.newBuilder()
-                                                        .setAction(
-                                                                Action.newBuilder().setActionType(
-                                                                        ActionType.DISMISS))
-                                                        .addAllUniqueId(expectedIds)
-                                                        .addAllUniqueContentDomain(
-                                                                expectedContentDomains)
-                                                        .addAllUniqueTable(expectedTables)
-                                                        .addAllUniqueSemanticProperties(
-                                                                expectedSemanticProperties)
-                                                        .addAllFeedActionQueryDataItem(
-                                                                expectedDataItems))
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    @Test
-    public void testHandleResponse() throws Exception {
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, mConsumer);
-
-        assertThat(mFakeProtocolAdapter.getLastResponse()).isEqualTo(Response.getDefaultInstance());
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mConsumedResult.isSuccessful()).isTrue();
-    }
-
-    @Test
-    public void testHandleResponse_notFound() throws Exception {
-        mFakeNetworkClient.addResponse(mFailingResponse);
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, mConsumer);
-
-        verify(mScheduler).onRequestError(NOT_FOUND);
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mConsumedResult.isSuccessful()).isFalse();
-    }
-
-    @Test
-    public void testHandleResponse_pageNotFound() throws Exception {
-        mFakeNetworkClient.addResponse(mFailingResponse);
-        StreamToken token =
-                StreamToken.newBuilder()
-                        .setNextPageToken(ByteString.copyFrom("abc", Charset.defaultCharset()))
-                        .build();
-        mFakeThreadUtils.enforceMainThread(false);
-        mRequestManager.loadMore(token, ConsistencyToken.getDefaultInstance(), mConsumer);
-
-        verify(mScheduler, never()).onRequestError(NOT_FOUND);
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mConsumedResult.isSuccessful()).isFalse();
-    }
-
-    @Test
-    public void testHandleResponse_missingLengthPrefixNotSupported() {
-        mFakeNetworkClient.addResponse(new HttpResponse(
-                /* responseCode= */ 200, Response.getDefaultInstance().toByteArray(), false));
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, mConsumer);
-        assertThat(mConsumer.isCalled()).isTrue();
-        assertThat(mConsumedResult.isSuccessful()).isFalse();
-        assertThat(mFakeProtocolAdapter.getLastResponse()).isNull();
-    }
-
-    @Test
-    public void testGetWireRequestResponse_unknown() throws Exception {
-        testReason(RequestReason.UNKNOWN, FeedQuery.RequestReason.UNKNOWN_REQUEST_REASON,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_zeroState() throws Exception {
-        testReason(RequestReason.ZERO_STATE, FeedQuery.RequestReason.ZERO_STATE_REFRESH,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_hostRequested() throws Exception {
-        testReason(RequestReason.HOST_REQUESTED, FeedQuery.RequestReason.SCHEDULED_REFRESH,
-                RequestHelper.PRIORITY_VALUE_BACKGROUND);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_openWithContent() throws Exception {
-        testReason(RequestReason.OPEN_WITH_CONTENT, FeedQuery.RequestReason.WITH_CONTENT,
-                RequestHelper.PRIORITY_VALUE_BACKGROUND);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_manualContinuation() throws Exception {
-        testReason(RequestReason.MANUAL_CONTINUATION, FeedQuery.RequestReason.NEXT_PAGE_SCROLL,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_automaticContinuation() throws Exception {
-        testReason(RequestReason.AUTOMATIC_CONTINUATION, FeedQuery.RequestReason.NEXT_PAGE_SCROLL,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_openWithoutContent() throws Exception {
-        testReason(RequestReason.OPEN_WITHOUT_CONTENT, FeedQuery.RequestReason.INITIAL_LOAD,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    public void testGetWireRequestResponse_clearAll() throws Exception {
-        testReason(RequestReason.CLEAR_ALL, FeedQuery.RequestReason.CLEAR_ALL,
-                RequestHelper.PRIORITY_VALUE_INTERACTIVE);
-    }
-
-    @Test
-    @Config(qualifiers = "en-rGB", sdk = VERSION_CODES.LOLLIPOP)
-    public void testClientInfo_postLollipop() throws Exception {
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", VERSION_CODES.LOLLIPOP);
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "RELEASE", "7.1.2b4.1");
-        ReflectionHelpers.setStaticField(Build.class, "SUPPORTED_ABIS", new String[] {"arm64-v8a"});
-        ReflectionHelpers.setStaticField(Build.class, "CPU_ABI", "armeabi");
-        ReflectionHelpers.setStaticField(Build.class, "TAGS", "release-keys");
-        when(mApplicationInfo.getAppType()).thenReturn(ApplicationInfo.AppType.SEARCH_APP);
-        when(mApplicationInfo.getArchitecture()).thenReturn(ApplicationInfo.Architecture.ARM64);
-        when(mApplicationInfo.getBuildType()).thenReturn(ApplicationInfo.BuildType.RELEASE);
-        when(mApplicationInfo.getVersionString()).thenReturn("1.2.3.4");
-
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-        Request expectedRequest =
-                Request.newBuilder()
-                        .setRequestVersion(RequestVersion.FEED_QUERY)
-                        .setExtension(FeedRequest.feedRequest,
-                                getTestFeedRequestBuilder()
-                                        .setFeedQuery(FeedQuery.newBuilder().setReason(
-                                                FeedQuery.RequestReason.SCHEDULED_REFRESH))
-                                        .setClientInfo(
-                                                ClientInfo.newBuilder()
-                                                        .setPlatformType(PlatformType.ANDROID_ID)
-                                                        .setPlatformVersion(
-                                                                Version.newBuilder()
-                                                                        .setMajor(7)
-                                                                        .setMinor(1)
-                                                                        .setBuild(2)
-                                                                        .setRevision(1)
-                                                                        .setArchitecture(
-                                                                                Architecture.ARM64)
-                                                                        .setBuildType(
-                                                                                BuildType.RELEASE)
-                                                                        .setApiVersion(
-                                                                                VERSION_CODES
-                                                                                        .LOLLIPOP)
-                                                                        .build())
-                                                        .setLocale(LocaleUtils.getLanguageTag(
-                                                                mContext))
-                                                        .setAppType(AppType.GSA)
-                                                        .setAppVersion(
-                                                                Version.newBuilder()
-                                                                        .setMajor(1)
-                                                                        .setMinor(2)
-                                                                        .setBuild(3)
-                                                                        .setRevision(4)
-                                                                        .setArchitecture(
-                                                                                Architecture.ARM64)
-                                                                        .setBuildType(
-                                                                                BuildType.RELEASE)
-                                                                        .build())
-                                                        .addDisplayInfo(
-                                                                DisplayInfo.newBuilder()
-                                                                        .setScreenDensity(1.0f)
-                                                                        .setScreenWidthInPixels(320)
-                                                                        .setScreenHeightInPixels(
-                                                                                470))
-                                                        .build())
-                                        .addClientCapability(Capability.SEND_FEEDBACK)
-                                        .addClientCapability(Capability.BASE_UI)
-                                        .build())
-                        .build();
-        assertThat(request).isEqualTo(expectedRequest);
-    }
-
-    private void testReason(@RequestReason int reason, FeedQuery.RequestReason expectedReason,
-            String expectedPriority) throws Exception {
-        mFakeNetworkClient.addResponse(mFailingResponse);
-        mRequestManager.triggerRefresh(reason, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        Request request = getRequestFromHttpRequest(httpRequest);
-        assertThat(request.getExtension(FeedRequest.feedRequest).getFeedQuery().getReason())
-                .isEqualTo(expectedReason);
-        assertThat(mFakeBasicLoggingApi.serverRequestReason).isEqualTo(reason);
-        assertThat(httpRequest.getUri().getQueryParameter(RequestHelper.PRIORITY_PARAM))
-                .isEqualTo(expectedPriority);
-    }
-
-    private static void assertHttpRequestFormattedCorrectly(
-            HttpRequest httpRequest, Context context) {
-        assertThat(httpRequest.getBody()).hasLength(0);
-        assertThat(httpRequest.getMethod()).isEqualTo(HttpMethod.GET);
-        assertThat(httpRequest.getUri().getQueryParameter("fmt")).isEqualTo("bin");
-        assertThat(httpRequest.getUri().getQueryParameter(RequestHelper.MOTHERSHIP_PARAM_PAYLOAD))
-                .isNotNull();
-        assertThat(httpRequest.getUri().getQueryParameter(RequestHelper.LOCALE_PARAM))
-                .isEqualTo(LocaleUtils.getLanguageTag(context));
-    }
-
-    private static HttpResponse createHttpResponse(int responseCode, Response response)
-            throws IOException {
-        byte[] rawResponse = response.toByteArray();
-        ByteBuffer buffer = ByteBuffer.allocate(rawResponse.length + (Integer.SIZE / 8));
-        CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(buffer);
-        codedOutputStream.writeUInt32NoTag(rawResponse.length);
-        codedOutputStream.writeRawBytes(rawResponse);
-        codedOutputStream.flush();
-        return new HttpResponse(responseCode, buffer.array(), false);
-    }
-
-    private static DismissActionWithSemanticProperties buildDismissAction(
-            long id, String contentDomain, String table, byte /*@Nullable*/[] semanticProperties) {
-        ContentId contentId = ContentId.newBuilder()
-                                      .setTable(table)
-                                      .setContentDomain(contentDomain)
-                                      .setId(id)
-                                      .build();
-        return new DismissActionWithSemanticProperties(contentId, semanticProperties);
-    }
-
-    private Request getRequestFromHttpRequest(HttpRequest httpRequest) throws Exception {
-        return Request.parseFrom(Base64.decode(httpRequest.getUri().getQueryParameter(
-                                                       RequestHelper.MOTHERSHIP_PARAM_PAYLOAD),
-                                         Base64.URL_SAFE),
-                mRegistry);
-    }
-
-    private static FeedRequest.Builder getTestFeedRequestBuilder() {
-        return FeedRequest.newBuilder()
-                .setConsistencyToken(ConsistencyToken.getDefaultInstance())
-                .setClientInfo(
-                        ClientInfo.newBuilder()
-                                .setPlatformType(PlatformType.ANDROID_ID)
-                                .setPlatformVersion(Version.newBuilder()
-                                                            .setMajor(4)
-                                                            .setMinor(4)
-                                                            .setBuild(3)
-                                                            .setArchitecture(Architecture.ARM)
-                                                            .setBuildType(BuildType.DEV)
-                                                            .setApiVersion(VERSION_CODES.KITKAT)
-                                                            .build())
-                                .setLocale(Locale.US.toLanguageTag())
-                                .setAppType(AppType.CHROME)
-                                .setAppVersion(Version.newBuilder()
-                                                       .setMajor(5)
-                                                       .setMinor(7)
-                                                       .setArchitecture(Architecture.ARM)
-                                                       .setBuildType(BuildType.DEV)
-                                                       .build())
-                                .addDisplayInfo(DisplayInfo.newBuilder()
-                                                        .setScreenDensity(1.0f)
-                                                        .setScreenWidthInPixels(320)
-                                                        .setScreenHeightInPixels(470))
-                                .build());
-    }
-
-    private void testCapabilityAdded(String configKey, Capability... capability) throws Exception {
-        Configuration configuration = new Configuration.Builder().put(configKey, true).build();
-        testCapabilityAddedWithConfig(configuration, capability);
-    }
-
-    private void testCapabilityAdded(Capability... capability) throws Exception {
-        Configuration configuration = new Configuration.Builder().build();
-        testCapabilityAddedWithConfig(configuration, capability);
-    }
-
-    private void testCapabilityAddedWithConfig(
-            Configuration configuration, Capability... capability) throws Exception {
-        mRequestManager = new FeedRequestManagerImpl(configuration, mFakeNetworkClient,
-                mFakeProtocolAdapter, new FeedExtensionRegistry(ArrayList::new), mScheduler,
-                mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mFakeActionReader, mContext,
-                mApplicationInfo, mFakeMainThreadRunner, mFakeBasicLoggingApi,
-                mFakeTooltipSupportedApi);
-        mRequestManager.triggerRefresh(RequestReason.HOST_REQUESTED, input -> {});
-
-        HttpRequest httpRequest = mFakeNetworkClient.getLatestRequest();
-        assertHttpRequestFormattedCorrectly(httpRequest, mContext);
-
-        Set<Capability> expectedCap = EnumSet.of(Capability.BASE_UI, Capability.SEND_FEEDBACK);
-        Collections.addAll(expectedCap, capability);
-
-        Request request = getRequestFromHttpRequest(httpRequest);
-        assertThat(request.getExtension(FeedRequest.feedRequest).getClientCapabilityList())
-                .containsExactlyElementsIn(expectedCap);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/RequestManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/RequestManagerImplTest.java
deleted file mode 100644
index 9eac15a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/RequestManagerImplTest.java
+++ /dev/null
@@ -1,53 +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.
-
-package org.chromium.chrome.browser.feed.library.feedrequestmanager;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.requestmanager.FeedRequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test of the {@link RequestManagerImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class RequestManagerImplTest {
-    @Mock
-    private FeedRequestManager mFeedRequestManager;
-    @Mock
-    private FeedSessionManager mFeedSessionManager;
-    @Mock
-    private Consumer<Result<Model>> mUpdateConsumer;
-
-    private RequestManagerImpl mRequestManager;
-
-    @Before
-    public void createRequestManager() {
-        initMocks(this);
-        mRequestManager = new RequestManagerImpl(mFeedRequestManager, mFeedSessionManager);
-        when(mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT))
-                .thenReturn(mUpdateConsumer);
-    }
-
-    @Test
-    public void testTriggerScheduledRefresh() {
-        mRequestManager.triggerScheduledRefresh();
-
-        verify(mFeedRequestManager).triggerRefresh(RequestReason.HOST_REQUESTED, mUpdateConsumer);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java
deleted file mode 100644
index 1e5d460..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java
+++ /dev/null
@@ -1,133 +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.
-
-package org.chromium.chrome.browser.feed.library.feedrequestmanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.testing.protocoladapter.FakeProtocolAdapter;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamUploadableAction;
-import org.chromium.components.feed.core.proto.wire.ActionPayloadForTestProto.ActionPayloadForTest;
-import org.chromium.components.feed.core.proto.wire.ActionPayloadProto.ActionPayload;
-import org.chromium.components.feed.core.proto.wire.ActionRequestProto.ActionRequest;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.FeedActionProto.FeedAction;
-import org.chromium.components.feed.core.proto.wire.FeedActionRequestProto.FeedActionRequest;
-import org.chromium.components.feed.core.proto.wire.SemanticPropertiesProto.SemanticProperties;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-/** Test of the {@link UploadableActionsRequestBuilder} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class UploadableActionsRequestBuilderTest {
-    private static final String CONTENT_ID = "contentId";
-    private static final int TIME = 100;
-    private static final long DURATION = 20;
-    private static final byte[] SEMANTIC_PROPERTIES_BYTES = new byte[] {0x1, 0xf};
-    private static final SemanticProperties SEMANTIC_PROPERTIES =
-            SemanticProperties.newBuilder()
-                    .setSemanticPropertiesData(ByteString.copyFrom(SEMANTIC_PROPERTIES_BYTES))
-                    .build();
-    private static final SemanticPropertiesWithId SEMANTIC_PROPERTIES_WITH_ID =
-            new SemanticPropertiesWithId(CONTENT_ID, SEMANTIC_PROPERTIES_BYTES);
-    private final ActionPayload mPayload =
-            ActionPayload.newBuilder()
-                    .setExtension(ActionPayloadForTest.actionPayloadForTestExtension,
-                            ActionPayloadForTest.newBuilder().setId(CONTENT_ID).build())
-                    .build();
-    private UploadableActionsRequestBuilder mBuilder;
-    private HashSet<StreamUploadableAction> mActionSet = new HashSet<>();
-    private ConsistencyToken mToken = ConsistencyToken.newBuilder()
-                                              .setToken(ByteString.copyFrom(new byte[] {0x1, 0xf}))
-                                              .build();
-    private ActionRequest.Builder mRequestBuilder;
-    private FeedActionRequest.Builder mFeedActionRequestBuilder;
-    private FakeProtocolAdapter mFakeProtocolAdapter;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mFakeProtocolAdapter = new FakeProtocolAdapter();
-        mFakeProtocolAdapter.addContentId(CONTENT_ID, ContentId.getDefaultInstance());
-        mBuilder = new UploadableActionsRequestBuilder(mFakeProtocolAdapter);
-        mActionSet.add(StreamUploadableAction.newBuilder()
-                               .setFeatureContentId(CONTENT_ID)
-                               .setPayload(mPayload)
-                               .setTimestampSeconds(TIME)
-                               .setDurationMs(DURATION)
-                               .build());
-        mRequestBuilder = ActionRequest.newBuilder().setRequestVersion(
-                ActionRequest.RequestVersion.FEED_UPLOAD_ACTION);
-        mFeedActionRequestBuilder = FeedActionRequest.newBuilder();
-    }
-
-    @Test
-    public void testUploadableActionsRequest_noToken() throws Exception {
-        FeedAction feedAction = FeedAction.newBuilder()
-                                        .setContentId(ContentId.getDefaultInstance())
-                                        .setActionPayload(mPayload)
-                                        .setClientData(FeedAction.ClientData.newBuilder()
-                                                               .setTimestampSeconds(TIME)
-                                                               .setDurationMs(DURATION)
-                                                               .build())
-                                        .build();
-        mFeedActionRequestBuilder.addFeedAction(feedAction);
-        mRequestBuilder.setExtension(
-                FeedActionRequest.feedActionRequest, mFeedActionRequestBuilder.build());
-
-        ActionRequest expectedResult = mRequestBuilder.build();
-        ActionRequest result = mBuilder.setActions(mActionSet).build();
-        assertThat(result).isEqualTo(expectedResult);
-    }
-
-    @Test
-    public void testUploadableActionsRequest_noActions() throws Exception {
-        mFeedActionRequestBuilder.setConsistencyToken(mToken);
-        mRequestBuilder.setExtension(
-                FeedActionRequest.feedActionRequest, mFeedActionRequestBuilder.build());
-
-        ActionRequest expectedResult = mRequestBuilder.build();
-        ActionRequest result = mBuilder.setConsistencyToken(mToken).build();
-        assertThat(result).isEqualTo(expectedResult);
-    }
-
-    @Test
-    public void testUploadableActionsRequest() throws Exception {
-        FeedAction feedAction = FeedAction.newBuilder()
-                                        .setContentId(ContentId.getDefaultInstance())
-                                        .setSemanticProperties(SEMANTIC_PROPERTIES)
-                                        .setActionPayload(mPayload)
-                                        .setClientData(FeedAction.ClientData.newBuilder()
-                                                               .setTimestampSeconds(TIME)
-                                                               .setDurationMs(DURATION)
-                                                               .build())
-                                        .build();
-        mFeedActionRequestBuilder.addFeedAction(feedAction);
-        mFeedActionRequestBuilder.setConsistencyToken(mToken);
-        mRequestBuilder.setExtension(
-                FeedActionRequest.feedActionRequest, mFeedActionRequestBuilder.build());
-
-        ActionRequest expectedResult = mRequestBuilder.build();
-        ActionRequest result =
-                mBuilder.setActions(mActionSet)
-                        .setConsistencyToken(mToken)
-                        .setSemanticProperties(Arrays.asList(SEMANTIC_PROPERTIES_WITH_ID))
-                        .build();
-        assertThat(result).isEqualTo(expectedResult);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/internal/UtilsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/internal/UtilsTest.java
deleted file mode 100644
index 0b24eab6..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/internal/UtilsTest.java
+++ /dev/null
@@ -1,48 +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.
-
-package org.chromium.chrome.browser.feed.library.feedrequestmanager.internal;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.wire.VersionProto.Version;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link Utils}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class UtilsTest {
-    @Test
-    public void fillVersionsFromString_allValid() {
-        Version.Builder builder = Version.newBuilder();
-        Utils.fillVersionsFromString(builder, "1.2.3.4");
-        Assert.assertEquals(1, builder.getMajor());
-        Assert.assertEquals(2, builder.getMinor());
-        Assert.assertEquals(3, builder.getBuild());
-        Assert.assertEquals(4, builder.getRevision());
-    }
-
-    @Test
-    public void fillVersionsFromString_ignoresExtraStrings() {
-        Version.Builder builder = Version.newBuilder();
-        Utils.fillVersionsFromString(builder, "1.2.3b5");
-        Assert.assertEquals(1, builder.getMajor());
-        Assert.assertEquals(2, builder.getMinor());
-        Assert.assertEquals(3, builder.getBuild());
-        Assert.assertFalse(builder.hasRevision());
-    }
-
-    @Test
-    public void fillVersionsFromString_emptyVersion() {
-        Version.Builder builder = Version.newBuilder();
-        Utils.fillVersionsFromString(builder, "");
-        Assert.assertFalse(builder.hasMajor());
-        Assert.assertFalse(builder.hasMinor());
-        Assert.assertFalse(builder.hasBuild());
-        Assert.assertFalse(builder.hasRevision());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/FeedSessionManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/FeedSessionManagerImplTest.java
deleted file mode 100644
index 6e2c16d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/FeedSessionManagerImplTest.java
+++ /dev/null
@@ -1,876 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.api.internal.store.Store.HEAD_SESSION_ID;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.ParameterizedRobolectricTestRunner;
-import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
-import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.SessionState;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.intern.Interner;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedAppLifecycleListener;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedLifecycleListener.LifecycleEvent;
-import org.chromium.chrome.browser.feed.library.feedmodelprovider.FeedModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.FeedSessionManagerImpl.SessionMutationTracker;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.FeedSessionManagerImpl.StreamSharedStateInterner;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.HeadSessionImpl;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.Session;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.SessionCache;
-import org.chromium.chrome.browser.feed.library.testing.actionmanager.FakeViewActionManager;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.testing.protocoladapter.FakeProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeActionUploadRequestManager;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamUploadableAction;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.libraries.testing.UiContextForTestProto.UiContextForTest;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.PietSharedStateItemProto.PietSharedStateItem;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.components.user_prefs.UserPrefsJni;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.List;
-
-/** Tests of the {@link FeedSessionManagerImpl} class. */
-@RunWith(ParameterizedRobolectricTestRunner.class)
-@Config(sdk = LocalRobolectricTestRunner.DEFAULT_SDK, manifest = Config.NONE)
-public class FeedSessionManagerImplTest {
-    private static final MutationContext EMPTY_MUTATION = new MutationContext.Builder().build();
-    private static final ContentId SHARED_STATE_ID = ContentId.newBuilder()
-                                                             .setContentDomain("piet-shared-state")
-                                                             .setId(1)
-                                                             .setTable("piet-shared-state")
-                                                             .build();
-    private static final String SESSION_ID = "session:1";
-    private static final int STORAGE_MISS_THRESHOLD = 4;
-
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final String mRootContentId = mIdGenerators.createRootContentId(0);
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    private Configuration mConfiguration;
-    private FakeActionUploadRequestManager mFakeActionUploadRequestManager;
-    private FakeBasicLoggingApi mFakeBasicLoggingApi;
-    private FakeMainThreadRunner mFakeMainThreadRunner;
-    private FakeProtocolAdapter mFakeProtocolAdapter;
-    private FakeFeedRequestManager mFakeRequestManager;
-    private FakeStore mFakeStore;
-    private FakeTaskQueue mFakeTaskQueue;
-    private FakeThreadUtils mFakeThreadUtils;
-    private FeedAppLifecycleListener mAppLifecycleListener;
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    private SchedulerApi mSchedulerApi;
-    @Mock
-    private UserPrefs.Natives mUserPrefsJniMock;
-    @Mock
-    private Profile mProfile;
-    @Mock
-    private PrefService mPrefService;
-    @Mock
-    private ActionManager mActionManager;
-
-    @Parameters
-    public static List<Object[]> data() {
-        return Arrays.asList(new Object[][] {{true}, {false}});
-    }
-
-    @Parameter(0)
-    public boolean mUploadingActionsEnabled;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
-        Profile.setLastUsedProfileForTesting(mProfile);
-        when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService);
-
-        mConfiguration = new Configuration.Builder()
-                                 .put(ConfigKey.UNDOABLE_ACTIONS_ENABLED, mUploadingActionsEnabled)
-                                 .put(ConfigKey.STORAGE_MISS_THRESHOLD, STORAGE_MISS_THRESHOLD)
-                                 .build();
-        mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-        mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-        mFakeMainThreadRunner =
-                FakeMainThreadRunner.runTasksImmediatelyWithThreadChecks(mFakeThreadUtils);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mAppLifecycleListener = new FeedAppLifecycleListener(mFakeThreadUtils);
-        mFakeStore = new FakeStore(mConfiguration, mFakeThreadUtils, mFakeTaskQueue, mFakeClock);
-        mFakeActionUploadRequestManager = new FakeActionUploadRequestManager(
-                mFakeStore, new FakeViewActionManager(mFakeStore), mFakeThreadUtils);
-        mFakeProtocolAdapter = new FakeProtocolAdapter();
-        mFakeRequestManager = new FakeFeedRequestManager(
-                mFakeThreadUtils, mFakeMainThreadRunner, mFakeProtocolAdapter, mFakeTaskQueue);
-        mFakeRequestManager.queueResponse(Response.getDefaultInstance());
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.NO_REQUEST_WITH_CONTENT);
-    }
-
-    @Test
-    public void testInitialization() {
-        StreamSharedState sharedState =
-                StreamSharedState.newBuilder()
-                        .setContentId(mIdGenerators.createFeatureContentId(0))
-                        .setPietSharedStateItem(PietSharedStateItem.getDefaultInstance())
-                        .build();
-        StreamStructure operation =
-                StreamStructure.newBuilder()
-                        .setContentId(mIdGenerators.createFeatureContentId(0))
-                        .setOperation(StreamStructure.Operation.UPDATE_OR_APPEND)
-                        .build();
-        mFakeStore.setSharedStates(sharedState).setStreamStructures(HEAD_SESSION_ID, operation);
-
-        FeedSessionManagerImpl sessionManager = createFeedSessionManager(mConfiguration);
-        assertThat(sessionManager.mInitialized.get()).isFalse();
-        sessionManager.initialize();
-        assertThat(sessionManager.mInitialized.get()).isTrue();
-        assertThat(sessionManager.getSharedStateCacheForTest()).hasSize(1);
-
-        SessionCache sessionCache = sessionManager.getSessionCacheForTest();
-        Session head = sessionCache.getHead();
-        assertThat(head).isInstanceOf(HeadSessionImpl.class);
-        String itemKey = mIdGenerators.createFeatureContentId(0);
-        assertThat(head.getContentInSession()).containsExactly(itemKey);
-    }
-
-    // This is testing a condition similar to the one that caused [INTERNAL LINK].
-    @Test
-    public void testInitialization_equalSharedStatesDifferentContentIds() throws Exception {
-        StreamSharedState sharedState1 =
-                StreamSharedState.newBuilder()
-                        .setContentId("shared-state-1")
-                        .setPietSharedStateItem(PietSharedStateItem.newBuilder().setPietSharedState(
-                                PietSharedState.newBuilder().addStylesheets(
-                                        Stylesheet.newBuilder().setStylesheetId(
-                                                "shared-stylesheet"))))
-                        .build();
-        StreamSharedState sharedState2 =
-                StreamSharedState.newBuilder()
-                        .setContentId("shared-state-2") //  Different ContentId
-                        .setPietSharedStateItem( // Equal PietSharedStateItem
-                                PietSharedStateItem.parseFrom(
-                                        sharedState1.getPietSharedStateItem().toByteString()))
-                        .build();
-        assertThat(sharedState1).isNotEqualTo(sharedState2);
-
-        // Initial PietSharedStateItem messages are equal but not the same between the 2 shared
-        // states.
-        assertThat(sharedState1.getPietSharedStateItem())
-                .isEqualTo(sharedState2.getPietSharedStateItem());
-        assertThat(sharedState1.getPietSharedStateItem())
-                .isNotSameInstanceAs(sharedState2.getPietSharedStateItem());
-
-        StreamStructure operation =
-                StreamStructure.newBuilder()
-                        .setContentId(mIdGenerators.createFeatureContentId(0))
-                        .setOperation(StreamStructure.Operation.UPDATE_OR_APPEND)
-                        .build();
-        mFakeStore.setSharedStates(sharedState1, sharedState2)
-                .setStreamStructures(HEAD_SESSION_ID, operation);
-
-        ContentId contentId1 = SHARED_STATE_ID.toBuilder().setId(1).build();
-        ContentId contentId2 = SHARED_STATE_ID.toBuilder().setId(2).build();
-        mFakeProtocolAdapter.addContentId("shared-state-1", contentId1)
-                .addContentId("shared-state-2", contentId2);
-
-        FeedSessionManagerImpl sessionManager = createFeedSessionManager(mConfiguration);
-        assertThat(sessionManager.mInitialized.get()).isFalse();
-        sessionManager.initialize();
-        assertThat(sessionManager.mInitialized.get()).isTrue();
-        assertThat(sessionManager.getSharedStateCacheForTest()).hasSize(2);
-
-        StreamSharedState cachedSharedState1 = sessionManager.getSharedState(contentId1);
-        StreamSharedState cachedSharedState2 = sessionManager.getSharedState(contentId2);
-        assertThat(cachedSharedState1).isEqualTo(sharedState1);
-        assertThat(cachedSharedState2).isEqualTo(sharedState2);
-
-        // Cached PietSharedStateItem messages the same between the 2 shared states (memoized).
-        assertThat(cachedSharedState1.getPietSharedStateItem())
-                .isSameInstanceAs(cachedSharedState2.getPietSharedStateItem());
-    }
-
-    @Test
-    public void testLifecycleInitialization() {
-        FeedSessionManagerImpl sessionManager = createFeedSessionManager(mConfiguration);
-        assertThat(sessionManager.mInitialized.get()).isFalse();
-        sessionManager.onLifecycleEvent(LifecycleEvent.INITIALIZE);
-        assertThat(sessionManager.mInitialized.get()).isTrue();
-        sessionManager.onLifecycleEvent(LifecycleEvent.INITIALIZE);
-        assertThat(sessionManager.mInitialized.get()).isTrue();
-    }
-
-    @Test
-    public void testSessionWithContent() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        int featureCnt = 3;
-        populateSession(sessionManager, featureCnt, 1, true, null);
-
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getRootFeature()).isNotNull();
-
-        ModelCursor cursor = modelProvider.getRootFeature().getCursor();
-        int cursorCount = 0;
-        while (cursor.getNextItem() != null) {
-            cursorCount++;
-        }
-        assertThat(cursorCount).isEqualTo(featureCnt);
-
-        // append a couple of others
-        populateSession(sessionManager, featureCnt, featureCnt + 1, false, null);
-
-        cursor = modelProvider.getRootFeature().getCursor();
-        cursorCount = 0;
-        while (cursor.getNextItem() != null) {
-            cursorCount++;
-        }
-        assertThat(cursorCount).isEqualTo(featureCnt * 2);
-    }
-
-    @Test
-    public void testNoRequestWithContent_populateIsImmediate() {
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.NO_REQUEST_WITH_CONTENT);
-
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager, 3, 1, true, null);
-        mFakeTaskQueue.resetCounts();
-
-        // Population will happen in an immediate task and no request is sent.
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeTaskQueue.getImmediateTaskCount()).isEqualTo(1);
-        assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.getUserFacingTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.isMakingRequest()).isFalse();
-    }
-
-    @Test
-    public void testRequestWithContent_populateIsImmediate() {
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.REQUEST_WITH_CONTENT);
-
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager, 3, 1, true, null);
-        mFakeTaskQueue.resetCounts();
-
-        // Population will happen immediately and a request is sent.
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeTaskQueue.getImmediateTaskCount()).isEqualTo(1);
-        assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.getUserFacingTaskCount()).isEqualTo(1);
-        assertThat(mFakeTaskQueue.isMakingRequest()).isTrue();
-    }
-
-    @Test
-    public void testRequestWithWait_populateIsUserFacing() {
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.REQUEST_WITH_WAIT);
-
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager, 3, 1, true, null);
-        mFakeTaskQueue.resetCounts();
-
-        // Population will happen in a user-facing task and a request is sent.
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeTaskQueue.getImmediateTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.getUserFacingTaskCount()).isEqualTo(2);
-        assertThat(mFakeTaskQueue.isMakingRequest()).isTrue();
-    }
-
-    @Test
-    public void testGetExistingSession_populateIsImmediate() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager,
-                /* featureCnt= */ 2,
-                /* idStart= */ 1,
-                /* reset= */ true,
-                /* sharedStateId= */ null);
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        String sessionId = modelProvider.getSessionId();
-        modelProvider.detachModelProvider();
-        mFakeTaskQueue.resetCounts();
-
-        // Population will happen in an immediate task.
-        modelProvider = getModelProvider(sessionManager, sessionId, UiContext.getDefaultInstance());
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeTaskQueue.getImmediateTaskCount()).isEqualTo(1);
-        assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(0);
-        assertThat(mFakeTaskQueue.getUserFacingTaskCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testMissingFeaturesBeyondThreshold_switchToEphemeralMode() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager, STORAGE_MISS_THRESHOLD, 1, true, null);
-        mFakeStore.clearContent();
-
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-        assertThat(mFakeBasicLoggingApi.lastInternalError)
-                .isEqualTo(InternalFeedError.STORAGE_MISS_BEYOND_THRESHOLD);
-    }
-
-    @Test
-    public void testMissingFeaturesAtThreshold_doesNotSwitchToEphemeralMode() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager, STORAGE_MISS_THRESHOLD - 1, 1, true, null);
-        mFakeStore.clearContent();
-
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider).isNotNull();
-        assertThat(mFakeStore.isEphemeralMode()).isFalse();
-        assertThat(mFakeBasicLoggingApi.lastInternalError)
-                .isEqualTo(InternalFeedError.CONTENT_STORAGE_MISSING_ITEM);
-    }
-
-    @Test
-    public void testNoCardsError() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        sessionManager.getUpdateConsumer(EMPTY_MUTATION).accept(Result.failure());
-
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider.getRootFeature()).isNull();
-
-        // Verify the failed session is correct
-        SessionCache sessionCache = sessionManager.getSessionCacheForTest();
-        assertThat(sessionCache.getAttachedSessions()).hasSize(1);
-        Session session = sessionCache.getAttached(modelProvider.getSessionId());
-        assertThat(session).isNotNull();
-    }
-
-    @Test
-    public void testNoCardsError_populatedHeadSuppressesError() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        populateSession(sessionManager,
-                /* featureCnt= */ 2,
-                /* idStart= */ 1,
-                /* reset= */ true,
-                /* sharedStateId= */ null);
-        sessionManager.getUpdateConsumer(EMPTY_MUTATION).accept(Result.failure());
-
-        ModelProvider modelProvider = getModelProvider(sessionManager);
-        assertThat(modelProvider.getRootFeature()).isNotNull();
-
-        // Verify the failed session is correct
-        SessionCache sessionCache = sessionManager.getSessionCacheForTest();
-        assertThat(sessionCache.getAttachedSessions()).hasSize(1);
-        Session session = sessionCache.getAttached(modelProvider.getSessionId());
-        assertThat(session).isNotNull();
-    }
-
-    @Test
-    public void testModelErrorObserver() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        // verify this runs.  Another method that can'be be verified on a single thread since
-        // the noCardsError will be set and unset.
-        sessionManager.modelErrorObserver(null, new ModelError(ErrorType.NO_CARDS_ERROR, null));
-    }
-
-    @Test
-    public void testReset() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        int featureCnt = 3;
-        int fullFeatureCount = populateSession(sessionManager, featureCnt, 1, true, null);
-        assertThat(fullFeatureCount).isEqualTo(featureCnt + 1);
-
-        fullFeatureCount = populateSession(sessionManager, featureCnt, 1, true, null);
-        assertThat(fullFeatureCount).isEqualTo(featureCnt + 1);
-    }
-
-    @Test
-    public void testHandleToken() {
-        ByteString bytes = ByteString.copyFrom("continuation", Charset.defaultCharset());
-        StreamToken streamToken = StreamToken.newBuilder()
-                                          .setNextPageToken(bytes)
-                                          .setParentId(mRootContentId)
-                                          .build();
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        sessionManager.handleToken(SESSION_ID, streamToken);
-
-        assertThat(mFakeRequestManager.getLatestStreamToken()).isEqualTo(streamToken);
-        assertThat(mFakeStore.getContentById(SessionCache.CONSISTENCY_TOKEN_CONTENT_ID))
-                .hasSize(mUploadingActionsEnabled ? 1 : 0);
-    }
-
-    @Test
-    public void testForceRefresh() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        sessionManager.triggerRefresh(
-                SESSION_ID, RequestReason.ZERO_STATE, UiContext.getDefaultInstance());
-
-        assertThat(mFakeRequestManager.getLatestRequestReason())
-                .isEqualTo(RequestReason.ZERO_STATE);
-    }
-
-    @Test
-    public void testForceRefresh_scheduledRefresh() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        sessionManager.triggerRefresh(
-                SESSION_ID, RequestReason.HOST_REQUESTED, UiContext.getDefaultInstance());
-
-        assertThat(mFakeRequestManager.getLatestRequestReason())
-                .isEqualTo(RequestReason.HOST_REQUESTED);
-    }
-
-    @Test
-    public void testGetSharedState() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        String sharedStateId = mIdGenerators.createSharedStateContentId(0);
-        ContentId undefinedSharedStateId = ContentId.newBuilder()
-                                                   .setContentDomain("shared-state")
-                                                   .setId(5)
-                                                   .setTable("shared-states")
-                                                   .build();
-        String undefinedStreamSharedStateId =
-                mIdGenerators.createSharedStateContentId(undefinedSharedStateId.getId());
-        mFakeProtocolAdapter.addContentId(sharedStateId, SHARED_STATE_ID)
-                .addContentId(undefinedStreamSharedStateId, undefinedSharedStateId);
-
-        populateSession(sessionManager, 3, 1, true, sharedStateId);
-        assertThat(sessionManager.getSharedState(SHARED_STATE_ID)).isNotNull();
-
-        // test the null condition
-        assertThat(sessionManager.getSharedState(undefinedSharedStateId)).isNull();
-    }
-
-    @Test
-    public void testUpdateConsumer() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-        Consumer<Result<Model>> updateConsumer = sessionManager.getUpdateConsumer(EMPTY_MUTATION);
-        assertThat(updateConsumer).isInstanceOf(SessionMutationTracker.class);
-        assertThat(sessionManager.mOutstandingMutations).hasSize(1);
-        assertThat(sessionManager.mOutstandingMutations).contains(updateConsumer);
-        updateConsumer.accept(Result.success(Model.empty()));
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-    }
-
-    @Test
-    public void testUpdateConsumer_clearAll() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-        Consumer<Result<Model>> updateConsumer = sessionManager.getUpdateConsumer(EMPTY_MUTATION);
-        assertThat(sessionManager.mOutstandingMutations).hasSize(1);
-        mAppLifecycleListener.onClearAll();
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-
-        // verify this still runs (as a noop)
-        updateConsumer.accept(Result.success(Model.empty()));
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-    }
-
-    @Test
-    public void testUpdateConsumer_clearAllWithRefresh() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-        Consumer<Result<Model>> updateConsumer = sessionManager.getUpdateConsumer(EMPTY_MUTATION);
-        assertThat(sessionManager.mOutstandingMutations).hasSize(1);
-        mAppLifecycleListener.onClearAllWithRefresh();
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-
-        // verify this still runs (as a noop)
-        updateConsumer.accept(Result.success(Model.empty()));
-        assertThat(sessionManager.mOutstandingMutations).isEmpty();
-    }
-
-    @Test
-    public void testEdit_semanticProperties() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-
-        ByteString semanticData = ByteString.copyFromUtf8("helloWorld");
-        StreamDataOperation streamDataOperation =
-                StreamDataOperation.newBuilder()
-                        .setStreamPayload(StreamPayload.newBuilder().setSemanticData(semanticData))
-                        .setStreamStructure(StreamStructure.newBuilder()
-                                                    .setContentId(mRootContentId)
-                                                    .setOperation(Operation.UPDATE_OR_APPEND))
-                        .build();
-
-        Consumer<Result<Model>> updateConsumer = sessionManager.getUpdateConsumer(EMPTY_MUTATION);
-        Result<Model> result = Result.success(Model.of(ImmutableList.of(streamDataOperation)));
-        updateConsumer.accept(result);
-
-        assertThat(mFakeStore.getContentById(mRootContentId))
-                .contains(new SemanticPropertiesWithId(mRootContentId, semanticData.toByteArray()));
-    }
-
-    @Test
-    public void testSwitchToEphemeralMode() {
-        FeedSessionManagerImpl sessionManager = getUninitializedSessionManager();
-        mFakeThreadUtils.enforceMainThread(false);
-        sessionManager.switchToEphemeralMode("An Error Message");
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-    }
-
-    @Test
-    public void testOnSwitchToEphemeralMode() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        String sharedStateId = mIdGenerators.createSharedStateContentId(0);
-
-        int featureCount = 3;
-        populateSession(sessionManager, featureCount, 1, true, sharedStateId);
-
-        assertThat(sessionManager.getSharedStateCacheForTest()).hasSize(1);
-        SessionCache sessionCache = sessionManager.getSessionCacheForTest();
-        assertThat(sessionCache.getAttachedSessions()).isEmpty();
-        Session session = sessionCache.getHead();
-        assertThat(session).isNotNull();
-        assertThat(session.getContentInSession()).hasSize(featureCount + 1);
-
-        mFakeThreadUtils.enforceMainThread(false);
-        sessionManager.onSwitchToEphemeralMode();
-
-        assertThat(sessionManager.getSharedStateCacheForTest()).isEmpty();
-        assertThat(sessionCache.getAttachedSessions()).isEmpty();
-        session = sessionCache.getHead();
-        assertThat(session).isNotNull();
-        assertThat(session.getContentInSession()).isEmpty();
-    }
-
-    @Test
-    public void testErrors_initializationSharedStateError() {
-        mFakeStore.setAllowGetSharedStates(false);
-        FeedSessionManagerImpl sessionManager = getUninitializedSessionManager();
-        sessionManager.initialize();
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-    }
-
-    @Test
-    public void testErrors_initializationStreamStructureError() {
-        mFakeStore.setAllowGetStreamStructures(false);
-        FeedSessionManagerImpl sessionManager = getUninitializedSessionManager();
-        sessionManager.initialize();
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-    }
-
-    @Test
-    public void testErrors_createNewSessionError() {
-        mFakeStore.setAllowCreateNewSession(false);
-        FeedSessionManagerImpl sessionManager = getUninitializedSessionManager();
-        sessionManager.initialize();
-        populateSession(sessionManager, 5, 1, true, null);
-
-        ModelProvider unused = getModelProvider(sessionManager);
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-    }
-
-    @Test
-    public void testErrors_getStreamStructuresError() {
-        FeedSessionManagerImpl sessionManager = getUninitializedSessionManager();
-        sessionManager.initialize();
-        mFakeStore.setAllowGetStreamStructures(false);
-        populateSession(sessionManager, 5, 1, true, null);
-
-        ModelProvider unused = getModelProvider(sessionManager);
-        assertThat(mFakeStore.isEphemeralMode()).isTrue();
-    }
-
-    @Test
-    public void testTriggerUploadActions() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager(
-                new Configuration.Builder().put(ConfigKey.UNDOABLE_ACTIONS_ENABLED, true).build());
-        ImmutableSet<StreamUploadableAction> actionSet =
-                ImmutableSet.of(StreamUploadableAction.getDefaultInstance());
-        ConsistencyToken token = ConsistencyToken.newBuilder()
-                                         .setToken(ByteString.copyFrom(new byte[] {0x1, 0xf}))
-                                         .build();
-        mFakeThreadUtils.enforceMainThread(false);
-        sessionManager.getConsistencyTokenConsumer().accept(Result.success(token));
-        sessionManager.triggerUploadActions(actionSet);
-        assertThat(mFakeActionUploadRequestManager.getLatestActions())
-                .containsExactlyElementsIn(actionSet);
-    }
-
-    @Test
-    public void testGetConsistencyToken() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager(
-                new Configuration.Builder().put(ConfigKey.UNDOABLE_ACTIONS_ENABLED, true).build());
-        ConsistencyToken token = ConsistencyToken.newBuilder()
-                                         .setToken(ByteString.copyFrom(new byte[] {0x1, 0xf}))
-                                         .build();
-        mFakeThreadUtils.enforceMainThread(false);
-        sessionManager.getConsistencyTokenConsumer().accept(Result.success(token));
-        assertThat(sessionManager.getConsistencyToken()).isEqualTo(token);
-    }
-
-    @Test
-    public void testGetConsistencyTokenEmpty() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        mFakeThreadUtils.enforceMainThread(false);
-        assertThat(sessionManager.getConsistencyToken())
-                .isEqualTo(ConsistencyToken.getDefaultInstance());
-    }
-
-    @Test
-    public void testFetchActionsAndUpload() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-        ConsistencyToken token = ConsistencyToken.newBuilder()
-                                         .setToken(ByteString.copyFrom(new byte[] {0x1, 0xf}))
-                                         .build();
-        ConsistencyToken expectedToken =
-                mUploadingActionsEnabled ? token : ConsistencyToken.getDefaultInstance();
-        Consumer<Result<ConsistencyToken>> consumer = result -> {
-            assertThat(result.isSuccessful()).isTrue();
-            assertThat(result.getValue()).isEqualTo(expectedToken);
-        };
-        mFakeActionUploadRequestManager.setResult(Result.success(token));
-        mFakeThreadUtils.enforceMainThread(false);
-        sessionManager.getConsistencyTokenConsumer().accept(Result.success(token));
-        sessionManager.fetchActionsAndUpload(consumer);
-        assertThat(mFakeActionUploadRequestManager.getLatestActions()).isNotNull();
-    }
-
-    @Test
-    public void testStreamSharedStateInterner() {
-        Interner<StreamSharedState> interner = new StreamSharedStateInterner();
-        StreamSharedState first =
-                StreamSharedState.newBuilder()
-                        .setContentId("foo")
-                        .setPietSharedStateItem(PietSharedStateItem.newBuilder().setPietSharedState(
-                                PietSharedState.newBuilder().addTemplates(
-                                        Template.newBuilder().setTemplateId("equal"))))
-                        .build();
-        StreamSharedState second =
-                StreamSharedState.newBuilder()
-                        .setContentId("baz")
-                        .setPietSharedStateItem(PietSharedStateItem.newBuilder().setPietSharedState(
-                                PietSharedState.newBuilder().addTemplates(
-                                        Template.newBuilder().setTemplateId("equal"))))
-                        .build();
-        StreamSharedState third =
-                StreamSharedState.newBuilder()
-                        .setContentId("bar")
-                        .setPietSharedStateItem(PietSharedStateItem.newBuilder().setPietSharedState(
-                                PietSharedState.newBuilder().addTemplates(
-                                        Template.newBuilder().setTemplateId("different"))))
-                        .build();
-        assertThat(first).isNotSameInstanceAs(second);
-        assertThat(first.getPietSharedStateItem()).isEqualTo(second.getPietSharedStateItem());
-        assertThat(first).isNotEqualTo(third);
-        assertThat(first.getPietSharedStateItem()).isNotEqualTo(third.getPietSharedStateItem());
-
-        // Pool is empty so first is added/returned.
-        StreamSharedState internedFirst = interner.intern(first);
-        assertThat(interner.size()).isEqualTo(1);
-        assertThat(internedFirst).isSameInstanceAs(first);
-
-        // Pool already has an identical inner PietSharedStateItem proto, which is used.
-        StreamSharedState internedSecond = interner.intern(second);
-        assertThat(interner.size()).isEqualTo(1);
-        // The returned proto is equal to second, but its internal PietSharedStateItem is the same
-        // as the one in first (memoized).
-        assertThat(internedSecond).isNotSameInstanceAs(second);
-        assertThat(internedSecond).isEqualTo(second);
-        assertThat(internedSecond.getPietSharedStateItem())
-                .isSameInstanceAs(first.getPietSharedStateItem());
-
-        // Third has a new PietSharedStateItem (not equal with any previous) so it is added to the
-        // pool.
-        StreamSharedState internedThird = interner.intern(third);
-        assertThat(interner.size()).isEqualTo(2);
-        assertThat(internedThird).isSameInstanceAs(third);
-    }
-
-    @Test
-    public void testGetNewSession() {
-        FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
-
-        UiContext uiContext = UiContext.newBuilder()
-                                      .setExtension(UiContextForTest.uiContextForTest,
-                                              UiContextForTest.newBuilder().setValue(3).build())
-                                      .build();
-
-        ModelProvider modelProvider = getModelProvider(sessionManager, uiContext);
-
-        sessionManager.getNewSession(modelProvider, /* viewDepthProvider= */ null, uiContext);
-        ModelProviderObserver modelProviderObserver = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(modelProviderObserver);
-
-        verify(modelProviderObserver).onSessionStart(uiContext);
-    }
-
-    @Test
-    @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void testLifecycleEventsWhenConditionalUploadFeatureEnabled() {
-        when(mPrefService.getBoolean(Pref.HAS_REACHED_CLICK_AND_VIEW_ACTIONS_UPLOAD_CONDITIONS))
-                .thenReturn(false);
-
-        FeedSessionManagerImpl sessionManager = createFeedSessionManager(mConfiguration);
-
-        sessionManager.onLifecycleEvent(LifecycleEvent.ENTER_FOREGROUND);
-        sessionManager.onLifecycleEvent(LifecycleEvent.ENTER_BACKGROUND);
-        sessionManager.onLifecycleEvent(LifecycleEvent.SIGNED_IN);
-        sessionManager.onLifecycleEvent(LifecycleEvent.SIGNED_OUT);
-
-        verify(mActionManager, times(4)).setCanUploadClicksAndViewsWhenNoticeCardIsPresent(false);
-    }
-
-    @Test
-    @Features.DisableFeatures(ChromeFeatureList.INTEREST_FEEDV1_CLICKS_AND_VIEWS_CONDITIONAL_UPLOAD)
-    public void testLifecycleEventsWhenConditionalUploadFeatureDisabled() {
-        when(mPrefService.getBoolean(Pref.HAS_REACHED_CLICK_AND_VIEW_ACTIONS_UPLOAD_CONDITIONS))
-                .thenReturn(false);
-
-        FeedSessionManagerImpl sessionManager = createFeedSessionManager(mConfiguration);
-
-        sessionManager.onLifecycleEvent(LifecycleEvent.ENTER_FOREGROUND);
-        sessionManager.onLifecycleEvent(LifecycleEvent.ENTER_BACKGROUND);
-        sessionManager.onLifecycleEvent(LifecycleEvent.SIGNED_IN);
-        sessionManager.onLifecycleEvent(LifecycleEvent.SIGNED_OUT);
-
-        verify(mActionManager, times(4)).setCanUploadClicksAndViewsWhenNoticeCardIsPresent(true);
-    }
-
-    private int populateSession(FeedSessionManagerImpl sessionManager, int featureCnt, int idStart,
-            boolean reset,
-            /*@Nullable*/ String sharedStateId) {
-        int operationCount = 0;
-
-        InternalProtocolBuilder internalProtocolBuilder = new InternalProtocolBuilder();
-        if (reset) {
-            internalProtocolBuilder.addClearOperation().addRootFeature();
-            operationCount++;
-        }
-        for (int i = 0; i < featureCnt; i++) {
-            internalProtocolBuilder.addFeature(
-                    mContentIdGenerators.createFeatureContentId(idStart++),
-                    mIdGenerators.createRootContentId(0));
-            operationCount++;
-        }
-        if (sharedStateId != null) {
-            internalProtocolBuilder.addSharedState(sharedStateId);
-            operationCount++;
-        }
-        Consumer<Result<Model>> updateConsumer = sessionManager.getUpdateConsumer(EMPTY_MUTATION);
-        updateConsumer.accept(Result.success(Model.of(internalProtocolBuilder.build())));
-        return operationCount;
-    }
-
-    private ModelProvider getModelProvider(FeedSessionManager sessionManager) {
-        return getModelProvider(
-                sessionManager, /* sessionId= */ null, UiContext.getDefaultInstance());
-    }
-
-    private ModelProvider getModelProvider(FeedSessionManager sessionManager, UiContext uiContext) {
-        return getModelProvider(sessionManager, /* sessionId= */ null, uiContext);
-    }
-
-    private ModelProvider getModelProvider(
-            FeedSessionManager sessionManager, String sessionId, UiContext uiContext) {
-        ModelProviderFactory modelProviderFactory = new FeedModelProviderFactory(sessionManager,
-                mFakeThreadUtils, mTimingUtils, mFakeTaskQueue, mFakeMainThreadRunner,
-                mConfiguration, mFakeBasicLoggingApi);
-        if (sessionId == null) {
-            return modelProviderFactory.createNew(/* viewDepthProvider= */ null, uiContext);
-        } else {
-            return modelProviderFactory.create(sessionId, uiContext);
-        }
-    }
-
-    private FeedSessionManagerImpl getInitializedSessionManager() {
-        return getInitializedSessionManager(mConfiguration);
-    }
-
-    private FeedSessionManagerImpl getInitializedSessionManager(Configuration config) {
-        FeedSessionManagerImpl fsm = createFeedSessionManager(config);
-        fsm.initialize();
-        return fsm;
-    }
-
-    private FeedSessionManagerImpl getUninitializedSessionManager() {
-        return createFeedSessionManager(mConfiguration);
-    }
-
-    private FeedSessionManagerImpl createFeedSessionManager(Configuration configuration) {
-        return new FeedSessionManagerFactory(mFakeTaskQueue, mFakeStore, mTimingUtils,
-                mFakeThreadUtils, mFakeProtocolAdapter, mFakeRequestManager,
-                mFakeActionUploadRequestManager, mSchedulerApi, configuration, mFakeClock,
-                mAppLifecycleListener, mFakeMainThreadRunner, mFakeBasicLoggingApi, mActionManager)
-                .create();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/ContentCacheTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/ContentCacheTest.java
deleted file mode 100644
index 3542ae7..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/ContentCacheTest.java
+++ /dev/null
@@ -1,93 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ContentCache} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentCacheTest {
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-    }
-
-    @Test
-    public void testBasicCaching() {
-        ContentCache cache = new ContentCache();
-        assertThat(cache.size()).isEqualTo(0);
-
-        StreamPayload payload = StreamPayload.getDefaultInstance();
-        String contentId = mIdGenerators.createFeatureContentId(1);
-        cache.put(contentId, payload);
-        assertThat(cache.size()).isEqualTo(1);
-        assertThat(cache.get(contentId)).isEqualTo(payload);
-
-        String missingId = mIdGenerators.createFeatureContentId(2);
-        assertThat(cache.get(missingId)).isNull();
-    }
-
-    @Test
-    public void testLifecycle() {
-        ContentCache cache = new ContentCache();
-        assertThat(cache.size()).isEqualTo(0);
-
-        cache.startMutation();
-        assertThat(cache.size()).isEqualTo(0);
-
-        StreamPayload payload = StreamPayload.getDefaultInstance();
-        String contentId = mIdGenerators.createFeatureContentId(1);
-        cache.put(contentId, payload);
-        assertThat(cache.size()).isEqualTo(1);
-        assertThat(cache.get(contentId)).isEqualTo(payload);
-    }
-
-    @Test
-    public void testStartMutation_resetsCache() {
-        ContentCache cache = new ContentCache();
-        cache.put(mIdGenerators.createFeatureContentId(1), StreamPayload.getDefaultInstance());
-
-        cache.startMutation();
-        assertThat(cache.size()).isEqualTo(0);
-    }
-
-    @Test
-    public void testFinishMutation_resetsCache() {
-        ContentCache cache = new ContentCache();
-
-        cache.startMutation();
-        cache.put(mIdGenerators.createFeatureContentId(1), StreamPayload.getDefaultInstance());
-        cache.finishMutation();
-        assertThat(cache.size()).isEqualTo(0);
-    }
-
-    @Test
-    public void testReset() {
-        ContentCache cache = new ContentCache();
-        StreamPayload payload = StreamPayload.getDefaultInstance();
-        int featureCount = 4;
-        for (int i = 0; i < featureCount; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            cache.put(contentId, payload);
-        }
-        assertThat(cache.size()).isEqualTo(featureCount);
-        cache.reset();
-        assertThat(cache.size()).isEqualTo(0);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadAsStructureTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadAsStructureTest.java
deleted file mode 100644
index a1525ab..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadAsStructureTest.java
+++ /dev/null
@@ -1,252 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators.ROOT_PREFIX;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.HeadAsStructure.TreeNode;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests of the {@link HeadAsStructure}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HeadAsStructureTest {
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final String mRootContentId = mIdGenerators.createRootContentId(0);
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private final FakeClock mFakeClock = new FakeClock();
-
-    private FakeStore mFakeStore;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mFakeStore = new FakeStore(Configuration.getDefaultInstance(), mFakeThreadUtils,
-                new FakeTaskQueue(mFakeClock, mFakeThreadUtils), mFakeClock);
-        mFakeThreadUtils.enforceMainThread(false);
-    }
-
-    @Test
-    public void testUninitialized() {
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        Result<List<Void>> result = headAsStructure.filter(payload -> null);
-        assertThat(result.isSuccessful()).isFalse();
-    }
-
-    @Test
-    public void testInitialization() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        assertThat(headAsStructure.mRoot).isNotNull();
-        assertThat(headAsStructure.mContent).hasSize(4);
-        assertThat(headAsStructure.mTree).hasSize(4);
-        TreeNode root = headAsStructure.mContent.get(mRootContentId);
-        assertThat(root).isNotNull();
-        assertThat(headAsStructure.mRoot).isEqualTo(root);
-        List<TreeNode> rootChildren = headAsStructure.mTree.get(mRootContentId);
-        assertThat(rootChildren).isNotNull();
-        assertThat(rootChildren).hasSize(3);
-    }
-
-    @Test
-    public void testInitialization_withRemove() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        String contentId = mIdGenerators.createFeatureContentId(2);
-        protocolBuilder.removeFeature(contentId, mRootContentId);
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        assertThat(headAsStructure.mRoot).isNotNull();
-        assertThat(headAsStructure.mContent).hasSize(3);
-        assertThat(headAsStructure.mTree).hasSize(3);
-        List<TreeNode> rootChildren = headAsStructure.mTree.get(mRootContentId);
-        assertThat(rootChildren).isNotNull();
-        assertThat(rootChildren).hasSize(2);
-    }
-
-    @Test
-    public void testInitialization_doubleInitialization() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isFalse());
-    }
-
-    @Test
-    public void testInitialization_buildTreeFailure() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore.setAllowGetStreamStructures(false).setContent(
-                protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isFalse());
-    }
-
-    @Test
-    public void testInitialization_bindTreeFailure() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setAllowGetPayloads(false);
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isFalse());
-    }
-
-    @Test
-    public void testFiltering_payload() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        Result<List<StreamFeature>> results = headAsStructure.filter(node -> {
-            StreamPayload payload = node.getStreamPayload();
-            assertThat(payload).isNotNull();
-            if (payload.hasStreamFeature()) {
-                return payload.getStreamFeature();
-            } else {
-                return null;
-            }
-        });
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue()).hasSize(4);
-    }
-
-    @Test
-    public void testFiltering_childrenOfRoot() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        Result<List<String>> results = headAsStructure.filter(node
-                -> !node.getStreamStructure().getContentId().startsWith(ROOT_PREFIX)
-                        ? node.getStreamStructure().getContentId()
-                        : null);
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue()).hasSize(3);
-    }
-
-    @Test
-    public void testFiltering_withRemove() {
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        protocolBuilder.addClearOperation().addRootFeature();
-        for (int i = 0; i < 3; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            protocolBuilder.addFeature(contentId, mRootContentId);
-        }
-        String contentId = mIdGenerators.createFeatureContentId(2);
-        protocolBuilder.removeFeature(contentId, mRootContentId);
-        mFakeStore
-                .setStreamStructures(
-                        Store.HEAD_SESSION_ID, protocolBuilder.buildAsStreamStructure())
-                .setContent(protocolBuilder.buildAsPayloadWithId());
-
-        HeadAsStructure headAsStructure =
-                new HeadAsStructure(mFakeStore, mTimingUtils, mFakeThreadUtils);
-        headAsStructure.initialize(result -> assertThat(result.isSuccessful()).isTrue());
-        Result<List<StreamFeature>> results = headAsStructure.filter(node -> {
-            StreamPayload payload = node.getStreamPayload();
-            assertThat(payload).isNotNull();
-            if (payload.hasStreamFeature()) {
-                return payload.getStreamFeature();
-            } else {
-                return null;
-            }
-        });
-        assertThat(results.isSuccessful()).isTrue();
-        assertThat(results.getValue()).hasSize(3);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadSessionImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadSessionImplTest.java
deleted file mode 100644
index 406ac952..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadSessionImplTest.java
+++ /dev/null
@@ -1,333 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-import java.util.Set;
-
-/** Tests of the {@link HeadSessionImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class HeadSessionImplTest {
-    private static final int SCHEMA_VERSION = 1;
-
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withoutThreadChecks();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private final FakeStore mFakeStore = new FakeStore(Configuration.getDefaultInstance(),
-            mFakeThreadUtils, new FakeTaskQueue(mFakeClock, mFakeThreadUtils), mFakeClock);
-    private final HeadSessionImpl mHeadSession =
-            new HeadSessionImpl(mFakeStore, mTimingUtils, /* limitPageUpdatesInHead= */ false);
-
-    @Test
-    public void testMinimalSessionManager() {
-        assertThat(mHeadSession.getSessionId()).isEqualTo(Store.HEAD_SESSION_ID);
-    }
-
-    @Test
-    public void testInvalidateOnResetHead() {
-        assertThat(mHeadSession.invalidateOnResetHead()).isFalse();
-    }
-
-    @Test
-    public void testUpdateSession_features() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // 1 clear, 3 features
-        assertThat(streamStructures).hasSize(featureCnt + 1);
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-
-        // expect: 3 features
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-        assertThat(mHeadSession.getContentInSession())
-                .contains(mContentIdGenerators.createFeatureContentId(1));
-        assertThat(getContentInSession()).hasSize(featureCnt);
-    }
-
-    @Test
-    public void testReset() {
-        int featureCnt = 5;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(featureCnt + 1);
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-
-        mHeadSession.reset();
-        assertThat(mHeadSession.getContentInSession()).isEmpty();
-    }
-
-    @Test
-    public void testUpdateSession_token() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        protocolBuilder.addToken(mContentIdGenerators.createTokenContentId(1));
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // 1 clear, 3 features, token
-        assertThat(streamStructures).hasSize(5);
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-
-        // expect: 3 features, 1 token
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt + 1);
-        assertThat(mHeadSession.getContentInSession())
-                .contains(mContentIdGenerators.createFeatureContentId(1));
-        assertThat(getContentInSession()).hasSize(featureCnt + 1);
-    }
-
-    @Test
-    public void testUpdateFromToken() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        StreamToken token = StreamToken.newBuilder()
-                                    .setContentId(mContentIdGenerators.createTokenContentId(2))
-                                    .build();
-
-        // The token needs to be in the session so update its content IDs with the token.
-        List<StreamStructure> tokenStructures = new InternalProtocolBuilder()
-                                                        .addToken(token.getContentId())
-                                                        .buildAsStreamStructure();
-        mHeadSession.updateSession(false, tokenStructures, SCHEMA_VERSION, null);
-
-        MutationContext context = new MutationContext.Builder().setContinuationToken(token).build();
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, context);
-        // features 3, plus the token added above
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt + 1);
-    }
-
-    @Test
-    public void testUpdateFromToken_limitPageUpdatesInHead() {
-        HeadSessionImpl headSession =
-                new HeadSessionImpl(mFakeStore, mTimingUtils, /* limitPageUpdatesInHead= */ true);
-
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        StreamToken token = StreamToken.newBuilder()
-                                    .setContentId(mContentIdGenerators.createTokenContentId(2))
-                                    .build();
-
-        // The token needs to be in the session so update its content IDs with the token.
-        List<StreamStructure> tokenStructures = new InternalProtocolBuilder()
-                                                        .addToken(token.getContentId())
-                                                        .buildAsStreamStructure();
-        headSession.updateSession(false, tokenStructures, SCHEMA_VERSION, null);
-
-        MutationContext context = new MutationContext.Builder().setContinuationToken(token).build();
-        headSession.updateSession(false, streamStructures, SCHEMA_VERSION, context);
-        assertThat(headSession.getContentInSession()).hasSize(1);
-    }
-
-    @Test
-    public void testUpdateFromToken_notInSession() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        StreamToken token = StreamToken.newBuilder()
-                                    .setContentId(mContentIdGenerators.createTokenContentId(2))
-                                    .build();
-
-        // The token needs to be in the session, if not we ignore the update
-        MutationContext context = new MutationContext.Builder().setContinuationToken(token).build();
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, context);
-        assertThat(mHeadSession.getContentInSession()).isEmpty();
-    }
-
-    @Test
-    public void testUpdateSession_remove() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        protocolBuilder.removeFeature(mContentIdGenerators.createFeatureContentId(1),
-                mContentIdGenerators.createRootContentId(0));
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // 1 clear, 3 features, 1 remove
-        assertThat(streamStructures).hasSize(5);
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-
-        // expect: 2 features (3 added, then 1 removed)
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt - 1);
-        assertThat(mHeadSession.getContentInSession())
-                .contains(mContentIdGenerators.createFeatureContentId(2));
-        assertThat(mHeadSession.getContentInSession())
-                .contains(mContentIdGenerators.createFeatureContentId(3));
-        assertThat(getContentInSession()).hasSize(featureCnt - 1);
-    }
-
-    @Test
-    public void testUpdateSession_updates() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(4);
-
-        // 1 clear, 3 features
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-        assertThat(getContentInSession()).hasSize(featureCnt);
-
-        // Now we will update feature 2
-        protocolBuilder = new InternalProtocolBuilder();
-        addFeatures(protocolBuilder, 1, 2);
-        streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(1);
-
-        // 0 features
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-        assertThat(getContentInSession()).hasSize(featureCnt);
-    }
-
-    @Test
-    public void testUpdateSession_paging() {
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(4);
-
-        // 1 clear, 3 features
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-        assertThat(getContentInSession()).hasSize(featureCnt);
-
-        // Now we add two new features
-        int additionalFeatureCnt = 2;
-        protocolBuilder = new InternalProtocolBuilder();
-        addFeatures(protocolBuilder, additionalFeatureCnt, featureCnt + 1);
-        streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(additionalFeatureCnt);
-
-        // 0 features
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt + additionalFeatureCnt);
-        assertThat(getContentInSession()).hasSize(featureCnt + additionalFeatureCnt);
-    }
-
-    @Test
-    public void testUpdateSession_storeClearHead() {
-        mHeadSession.initializeSession(ImmutableList.of(), SCHEMA_VERSION);
-
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-        assertThat(streamStructures).hasSize(4);
-
-        // 1 clear, 3 features
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION + 1, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(featureCnt);
-        assertThat(getContentInSession()).hasSize(featureCnt);
-        assertThat(mHeadSession.getSchemaVersion()).isEqualTo(SCHEMA_VERSION);
-
-        // Clear head and add 2 features, make sure we have new content ids
-        int newFeatureCnt = 2;
-        protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, newFeatureCnt, featureCnt + 1);
-        streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // 2 features, 1 clear
-        assertThat(streamStructures).hasSize(3);
-
-        // 0 features
-        mFakeStore.clearHead();
-        mHeadSession.updateSession(false, streamStructures, SCHEMA_VERSION + 1, null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(newFeatureCnt);
-        assertThat(getContentInSession()).hasSize(newFeatureCnt);
-        assertThat(mHeadSession.getSchemaVersion()).isEqualTo(SCHEMA_VERSION);
-    }
-
-    @Test
-    public void testUpdateSession_clearHeadUpdatesSchemaVersion() {
-        mHeadSession.initializeSession(ImmutableList.of(), SCHEMA_VERSION);
-        mHeadSession.updateSession(
-                /* clearHead= */ true, ImmutableList.of(), SCHEMA_VERSION + 1,
-                /* mutationContext= */ null);
-        assertThat(mHeadSession.getSchemaVersion()).isEqualTo(SCHEMA_VERSION + 1);
-    }
-
-    @Test
-    public void testUpdateSession_schemaVersionUnchanged() {
-        mHeadSession.initializeSession(ImmutableList.of(), SCHEMA_VERSION);
-        mHeadSession.updateSession(
-                /* clearHead= */ false, ImmutableList.of(), SCHEMA_VERSION + 1,
-                /* mutationContext= */ null);
-        assertThat(mHeadSession.getSchemaVersion()).isEqualTo(SCHEMA_VERSION);
-    }
-
-    @Test
-    public void testUpdateSession_requiredContent() {
-        String contentId = mContentIdGenerators.createFeatureContentId(1);
-        InternalProtocolBuilder protocolBuilder =
-                new InternalProtocolBuilder().addRequiredContent(contentId);
-
-        mHeadSession.updateSession(
-                /* clearHead= */ false, protocolBuilder.buildAsStreamStructure(), SCHEMA_VERSION,
-                /* mutationContext= */ null);
-        assertThat(mHeadSession.getContentInSession()).hasSize(1);
-        assertThat(getContentInSession()).hasSize(1);
-    }
-
-    @Test
-    public void testInitializeSession_schemaVersion() {
-        int schemaVersion = 3;
-        mHeadSession.initializeSession(ImmutableList.of(), schemaVersion);
-        assertThat(mHeadSession.getSchemaVersion()).isEqualTo(schemaVersion);
-    }
-
-    private void addFeatures(InternalProtocolBuilder protocolBuilder, int featureCnt, int startId) {
-        for (int i = 0; i < featureCnt; i++) {
-            protocolBuilder.addFeature(mContentIdGenerators.createFeatureContentId(startId++),
-                    mContentIdGenerators.createRootContentId(0));
-        }
-    }
-
-    /** Re-read the session from disk and return the set of content. */
-    private Set<String> getContentInSession() {
-        HeadSessionImpl headSession =
-                new HeadSessionImpl(mFakeStore, mTimingUtils, /* limitPageUpdatesInHead= */ false);
-        headSession.initializeSession(
-                mFakeStore.getStreamStructures(Store.HEAD_SESSION_ID).getValue(),
-                /* schemaVersion= */ 0);
-        return headSession.getContentInSession();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionCacheTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionCacheTest.java
deleted file mode 100644
index 50a6ee19..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionCacheTest.java
+++ /dev/null
@@ -1,556 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeDirectExecutor;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelProvider;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.SessionMetadata;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSession;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSessions;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link SessionCache} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SessionCacheTest {
-    private static final long DEFAULT_LIFETIME_MS = 10;
-    private static final int SCHEMA_VERSION = 4;
-
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    private FakeStore mFakeStore;
-    private FakeTaskQueue mFakeTaskQueue;
-    private SessionFactory mSessionFactory;
-    private SessionCache mSessionCache;
-
-    protected final FakeModelProvider mFakeModelProvider = new FakeModelProvider();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeStore = new FakeStore(mConfiguration, mFakeThreadUtils, mFakeTaskQueue, mFakeClock);
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeTaskQueue.initialize(() -> {});
-        mSessionCache = getSessionCache();
-    }
-
-    @Test
-    public void testInitialization() {
-        int schemaVersion = 3;
-        populateHead();
-        mockStreamSessions(
-                StreamSessions.newBuilder()
-                        .addStreamSession(
-                                StreamSession.newBuilder()
-                                        .setSessionId(Store.HEAD_SESSION_ID)
-                                        .setSessionMetadata(
-                                                SessionMetadata.newBuilder().setSchemaVersion(
-                                                        schemaVersion)))
-                        .build());
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-        assertThat(mSessionCache.isHeadInitialized()).isFalse();
-        assertThat(mSessionCache.getHead()).isNotNull();
-        assertThat(mSessionCache.getHead().isHeadEmpty()).isTrue();
-        mSessionCache.initialize();
-
-        // Initialization adds $HEAD
-        assertThat(mSessionCache.isHeadInitialized()).isTrue();
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-        assertThat(mSessionCache.getHead()).isNotNull();
-        assertThat(mSessionCache.getHead().isHeadEmpty()).isFalse();
-        assertThat(mSessionCache.getHead().getSchemaVersion()).isEqualTo(schemaVersion);
-    }
-
-    @Test
-    public void testInitialization_accessibleContentShouldBeDeterminedAtGcTime() {
-        FakeDirectExecutor fakeDirectExecutor = FakeDirectExecutor.queueAllTasks(mFakeThreadUtils);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, fakeDirectExecutor);
-        mFakeStore = new FakeStore(mConfiguration, mFakeThreadUtils, mFakeTaskQueue, mFakeClock);
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeTaskQueue.initialize(() -> {});
-        mSessionCache = getSessionCache();
-
-        Session session = populateSession(1, 2, /* commitToStore= */ true);
-        mSessionCache.initialize();
-        setSessions(session);
-        fakeDirectExecutor.runAllTasks(); // Run GC.
-
-        assertThat(fakeDirectExecutor.hasTasks()).isFalse();
-        assertPayloads(/* featureCnt= */ 2);
-    }
-
-    @Test
-    public void testInitialization_contentGcShouldKeepSharedStates() {
-        mFakeStore.setContent("foo",
-                StreamPayload.newBuilder()
-                        .setStreamSharedState(
-                                StreamSharedState.newBuilder().setContentId("foo").build())
-                        .build());
-        mockStreamSessions(
-                StreamSessions.newBuilder()
-                        .addStreamSession(
-                                StreamSession.newBuilder()
-                                        .setSessionId(Store.HEAD_SESSION_ID)
-                                        .setSessionMetadata(
-                                                // clang-format off
-          SessionMetadata.newBuilder().setSchemaVersion(
-            SessionCache.MIN_SCHEMA_VERSION_FOR_PIET_SHARED_STATE_REQUIRED_CONTENT - 1)
-                                                // clang-format on
-                                                ))
-                        .build());
-
-        assertThat(mFakeStore.getSharedStates().getValue()).hasSize(1);
-        mSessionCache.initialize();
-        assertThat(mFakeStore.getSharedStates().getValue()).hasSize(1);
-    }
-
-    @Test
-    public void testInitialization_contentGcShouldDiscardSharedStates() {
-        mFakeStore.setContent("foo",
-                StreamPayload.newBuilder()
-                        .setStreamSharedState(
-                                StreamSharedState.newBuilder().setContentId("foo").build())
-                        .build());
-        mockStreamSessions(
-                StreamSessions.newBuilder()
-                        .addStreamSession(
-                                StreamSession.newBuilder()
-                                        .setSessionId(Store.HEAD_SESSION_ID)
-                                        .setSessionMetadata(
-                                                // clang-format off
-                                          SessionMetadata.newBuilder().setSchemaVersion(
-                    SessionCache.MIN_SCHEMA_VERSION_FOR_PIET_SHARED_STATE_REQUIRED_CONTENT)
-                                                // clang-format on
-                                                ))
-                        .build());
-
-        assertThat(mFakeStore.getSharedStates().getValue()).hasSize(1);
-        mSessionCache.initialize();
-        assertThat(mFakeStore.getSharedStates().getValue()).isEmpty();
-    }
-
-    @Test
-    public void testPutGet() {
-        mSessionCache.initialize();
-        Session session = populateSession(1, 2);
-        mSessionCache.putAttachedAndRetainMetadata(session.getSessionId(), session);
-
-        Session ret = mSessionCache.getAttached(session.getSessionId());
-        assertThat(ret).isEqualTo(session);
-    }
-
-    @Test
-    public void testPut_persisted() {
-        mSessionCache.initialize();
-        Session session = populateSession(1, 2);
-        mSessionCache.putAttached(
-                session.getSessionId(), /* creationTimeMillis= */ 0L, SCHEMA_VERSION, session);
-
-        Session ret = mSessionCache.getAttached(session.getSessionId());
-        assertThat(ret).isEqualTo(session);
-
-        session = populateSession(2, 2);
-        mSessionCache.putAttached(
-                session.getSessionId(), /* creationTimeMillis= */ 0L, SCHEMA_VERSION, session);
-
-        List<StreamSession> streamSessionList = mSessionCache.getPersistedSessions();
-        assertThat(streamSessionList).hasSize(3);
-    }
-
-    @Test
-    public void testRemove() {
-        mSessionCache.initialize();
-        Session session = populateSession(1, 2);
-        mSessionCache.putAttached(
-                session.getSessionId(), /* creationTimeMillis= */ 0L, SCHEMA_VERSION, session);
-
-        List<StreamSession> streamSessionList = mSessionCache.getPersistedSessions();
-        assertThat(streamSessionList).hasSize(2);
-
-        String id = session.getSessionId();
-        mSessionCache.removeAttached(id);
-        assertThat(mSessionCache.getAttached(id)).isNull();
-
-        streamSessionList = mSessionCache.getPersistedSessions();
-        assertThat(streamSessionList).hasSize(1);
-    }
-
-    @Test
-    public void testDetach() {
-        mSessionCache.initialize();
-        Session s1 = populateSession(1, 2);
-        String s1Id = s1.getSessionId();
-        mSessionCache.putAttachedAndRetainMetadata(s1Id, s1);
-
-        List<Session> sessions = mSessionCache.getAttachedSessions();
-        assertThat(sessions).hasSize(1);
-        assertThat(sessions).contains(s1);
-        assertThat(s1.getModelProvider()).isNotNull();
-
-        mSessionCache.detachModelProvider(s1Id);
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-        assertThat(s1.getModelProvider()).isNull();
-    }
-
-    @Test
-    public void testGetAttachedSessions() {
-        mSessionCache.initialize();
-        Session s1 = populateSession(1, 2);
-        mSessionCache.putAttachedAndRetainMetadata(s1.getSessionId(), s1);
-        Session s2 = populateSession(2, 2);
-        mSessionCache.putAttachedAndRetainMetadata(s2.getSessionId(), s2);
-
-        List<Session> sessions = mSessionCache.getAttachedSessions();
-        assertThat(sessions).hasSize(2);
-        assertThat(sessions).contains(mSessionCache.getAttached(s1.getSessionId()));
-        assertThat(sessions).contains(mSessionCache.getAttached(s2.getSessionId()));
-    }
-
-    @Test
-    public void testGetAllSessions() {
-        mSessionCache.initialize();
-        Session headSession = mSessionCache.getHead();
-
-        Session s1 = populateSession(1, 2, /* commitToStore= */ true);
-        String s1Id = s1.getSessionId();
-        mSessionCache.putAttached(s1Id, 1L, SCHEMA_VERSION, s1);
-
-        assertThat(mSessionCache.getAttachedSessions()).containsExactly(s1);
-        assertThat(mSessionCache.getAllSessions()).containsExactly(s1, headSession);
-
-        Session s2 = populateSession(2, 2, /* commitToStore= */ true);
-        String s2Id = s2.getSessionId();
-        mSessionCache.putAttached(s2Id, 2L, SCHEMA_VERSION, s2);
-
-        assertThat(mSessionCache.getAttachedSessions()).containsExactly(s1, s2);
-        assertThat(mSessionCache.getAllSessions()).containsExactly(s1, s2, headSession);
-
-        // Detach the session, which will throw it away from SessionCache.
-        mSessionCache.detachModelProvider(s1Id);
-        assertThat(mSessionCache.getAttachedSessions()).containsExactly(s2);
-
-        List<Session> allSessions = new ArrayList<>(mSessionCache.getAllSessions());
-        assertThat(allSessions).hasSize(3);
-        assertThat(allSessions).containsAtLeast(s2, headSession);
-        allSessions.remove(s2);
-        allSessions.remove(headSession);
-
-        // A new unbound session was created for the detached one, with the same ID.
-        Session unboundSession = allSessions.get(0);
-        assertThat(unboundSession.getSessionId()).isEqualTo(s1.getSessionId());
-        assertThat(unboundSession.getContentInSession()).isEqualTo(s1.getContentInSession());
-        assertThat(unboundSession).isNotSameInstanceAs(s1);
-    }
-
-    @Test
-    public void testReset_headOnly() {
-        mSessionCache.initialize();
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-
-        mSessionCache.reset();
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-    }
-
-    @Test
-    public void testReset_sessions() {
-        mSessionCache.initialize();
-        int sessionCount = 2;
-        for (int i = 0; i < sessionCount; i++) {
-            Session session = populateSession(i, 2);
-            mSessionCache.putAttachedAndRetainMetadata(session.getSessionId(), session);
-        }
-        List<Session> sessions = mSessionCache.getAttachedSessions();
-        assertThat(sessions).hasSize(sessionCount);
-
-        mSessionCache.reset();
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-    }
-
-    @Test
-    public void testIsSessionAlive() {
-        mSessionCache.initialize();
-        mFakeClock.set(DEFAULT_LIFETIME_MS + 2);
-        assertThat(mSessionCache.isSessionAlive("stream:1", SessionMetadata.getDefaultInstance()))
-                .isFalse();
-        assertThat(mSessionCache.isSessionAlive(
-                           Store.HEAD_SESSION_ID, SessionMetadata.getDefaultInstance()))
-                .isTrue();
-        assertThat(mSessionCache.isSessionAlive("stream:2",
-                           SessionMetadata.newBuilder()
-                                   .setCreationTimeMillis(DEFAULT_LIFETIME_MS - 1)
-                                   .build()))
-                .isTrue();
-    }
-
-    @Test
-    public void testGetPersistedSessions() {
-        StreamSession streamSession = StreamSession.getDefaultInstance();
-        mockStreamSessions(StreamSessions.newBuilder().addStreamSession(streamSession).build());
-
-        List<StreamSession> sessionList = mSessionCache.getPersistedSessions();
-        assertThat(sessionList).containsExactly(streamSession);
-    }
-
-    @Test
-    public void testCleanupJournals() {
-        String sessionId1 = "stream:1";
-        String sessionId2 = "stream:2";
-        mFakeStore.setStreamStructures(sessionId1, StreamStructure.getDefaultInstance())
-                .setStreamStructures(sessionId2, StreamStructure.getDefaultInstance());
-
-        Session s2 = mock(Session.class);
-        when(s2.getSessionId()).thenReturn(sessionId2);
-        setSessions(s2);
-        mSessionCache.cleanupSessionJournals();
-        assertThat(mFakeStore.getAllSessions().getValue()).containsExactly(sessionId2);
-        assertThat(mSessionCache.getAttached(sessionId2)).isEqualTo(s2);
-    }
-
-    @Test
-    public void testInitializePersistedSessions_emptyStreamSessions() {
-        mFakeClock.set(DEFAULT_LIFETIME_MS + 2);
-
-        mockStreamSessions(StreamSessions.getDefaultInstance());
-        mSessionCache.initialize();
-
-        assertThat(mSessionCache.getAttachedSessions()).isEmpty();
-    }
-
-    @Test
-    public void testInitializePersistedSessions_legacyStreamSession() {
-        StreamSessions streamSessions =
-                StreamSessions.newBuilder()
-                        .addStreamSession(StreamSession.newBuilder()
-                                                  .setSessionId("stream:1")
-                                                  .setLegacyTimeMillis(0))
-                        .addStreamSession(StreamSession.newBuilder()
-                                                  .setSessionId("stream:2")
-                                                  .setLegacyTimeMillis(DEFAULT_LIFETIME_MS - 1))
-                        .addStreamSession(StreamSession.newBuilder()
-                                                  .setSessionId(Store.HEAD_SESSION_ID)
-                                                  .setLegacyTimeMillis(1))
-                        .build();
-
-        mFakeClock.set(DEFAULT_LIFETIME_MS + 2);
-
-        mockStreamSessions(streamSessions);
-        mSessionCache.initialize();
-
-        assertThat(mSessionCache.hasSession("stream:1")).isFalse();
-        assertThat(mSessionCache.hasSession("stream:2")).isTrue();
-        assertThat(mSessionCache.getCreationTimeMillis("stream:2"))
-                .isEqualTo(DEFAULT_LIFETIME_MS - 1);
-        assertThat(mSessionCache.getHeadLastAddedTimeMillis()).isEqualTo(1);
-    }
-
-    @Test
-    public void testInitializePersistedSessions_sessionMetadata() {
-        StreamSessions streamSessions =
-                StreamSessions.newBuilder()
-                        .addStreamSession(
-                                StreamSession.newBuilder()
-                                        .setSessionId("stream:1")
-                                        .setSessionMetadata(SessionMetadata.getDefaultInstance()))
-                        .addStreamSession(
-                                StreamSession.newBuilder()
-                                        .setSessionId("stream:2")
-                                        .setSessionMetadata(
-                                                SessionMetadata.newBuilder().setCreationTimeMillis(
-                                                        DEFAULT_LIFETIME_MS - 1)))
-                        .build();
-
-        mFakeClock.set(DEFAULT_LIFETIME_MS + 2);
-
-        mockStreamSessions(streamSessions);
-        mSessionCache.initialize();
-
-        assertThat(mSessionCache.hasSession("stream:1")).isFalse();
-        assertThat(mSessionCache.hasSession("stream:2")).isTrue();
-        assertThat(mSessionCache.getCreationTimeMillis("stream:2"))
-                .isEqualTo(DEFAULT_LIFETIME_MS - 1);
-    }
-
-    @Test
-    public void testUpdateHeadMetadata() {
-        long currentTime = 2L;
-        int schemaVersion = 3;
-        StreamSessions streamSessions =
-                StreamSessions.newBuilder()
-                        .addStreamSession(StreamSession.newBuilder()
-                                                  .setSessionId(Store.HEAD_SESSION_ID)
-                                                  .setLegacyTimeMillis(1))
-                        .build();
-
-        mockStreamSessions(streamSessions);
-        mSessionCache.initialize();
-        mSessionCache.updateHeadMetadata(currentTime, schemaVersion);
-
-        List<Object> content = mFakeStore.getContentById(SessionCache.STREAM_SESSION_CONTENT_ID);
-        assertThat(content).hasSize(1);
-        assertThat(((PayloadWithId) content.get(0)).payload)
-                .isEqualTo(
-                        StreamPayload.newBuilder()
-                                .setStreamSessions(StreamSessions.newBuilder().addStreamSession(
-                                        StreamSession.newBuilder()
-                                                .setSessionId(Store.HEAD_SESSION_ID)
-                                                .setSessionMetadata(
-                                                        SessionMetadata.newBuilder()
-                                                                .setLastAddedTimeMillis(currentTime)
-                                                                .setSchemaVersion(schemaVersion))))
-                                .build());
-    }
-
-    @Test
-    public void testUpdatePersistedSessions() {
-        mSessionCache.initialize();
-
-        // persist HEAD into the store
-        mSessionCache.updatePersistedSessionsMetadata();
-        List<StreamSession> streamSessionList = mSessionCache.getPersistedSessions();
-        assertThat(streamSessionList).hasSize(1);
-
-        // add additional sessions
-        StreamSession session1 =
-                StreamSession.newBuilder()
-                        .setSessionId("stream:1")
-                        .setSessionMetadata(SessionMetadata.newBuilder()
-                                                    .setCreationTimeMillis(0L)
-                                                    .setSchemaVersion(SCHEMA_VERSION))
-                        .build();
-        StreamSession session2 =
-                StreamSession.newBuilder()
-                        .setSessionId("stream:2")
-                        .setSessionMetadata(SessionMetadata.newBuilder()
-                                                    .setCreationTimeMillis(0L)
-                                                    .setSchemaVersion(SCHEMA_VERSION))
-                        .build();
-        Session s1 = mock(Session.class);
-        when(s1.getSessionId()).thenReturn(session1.getSessionId());
-        Session s2 = mock(Session.class);
-        when(s2.getSessionId()).thenReturn(session2.getSessionId());
-        setSessions(s1, s2);
-
-        mSessionCache.updatePersistedSessionsMetadata();
-        streamSessionList = mSessionCache.getPersistedSessions();
-        assertThat(streamSessionList).hasSize(3);
-        assertThat(streamSessionList).contains(session1);
-        assertThat(streamSessionList).contains(session2);
-    }
-
-    @Test
-    public void testErrors_persistedSession() {
-        mSessionCache.initialize();
-        mSessionCache.updatePersistedSessionsMetadata();
-
-        mFakeStore.setAllowGetPayloads(false);
-        assertThat(mSessionCache.getPersistedSessions()).isEmpty();
-    }
-
-    private SessionCache getSessionCache() {
-        mSessionFactory = new SessionFactory(
-                mFakeStore, mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mConfiguration);
-        return new SessionCache(mFakeStore, mFakeTaskQueue, mSessionFactory, 10, mTimingUtils,
-                mFakeThreadUtils, mFakeClock);
-    }
-
-    private void mockStreamSessions(StreamSessions streamSessions) {
-        mFakeStore.setContent(SessionCache.STREAM_SESSION_CONTENT_ID,
-                StreamPayload.newBuilder().setStreamSessions(streamSessions).build());
-    }
-
-    private Session populateSession(int id, int featureCnt) {
-        return populateSession(id, featureCnt, /* commitToStore= */ false);
-    }
-
-    private Session populateSession(int id, int featureCnt, boolean commitToStore) {
-        InitializableSession session = mSessionFactory.getSession();
-        String rootId = mIdGenerators.createRootContentId(1);
-        List<StreamStructure> head = new ArrayList<>();
-        head.add(StreamStructure.newBuilder()
-                         .setOperation(Operation.UPDATE_OR_APPEND)
-                         .setContentId(rootId)
-                         .build());
-        for (int i = 0; i < featureCnt; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            head.add(StreamStructure.newBuilder()
-                             .setOperation(Operation.UPDATE_OR_APPEND)
-                             .setContentId(contentId)
-                             .setParentContentId(rootId)
-                             .build());
-            if (commitToStore) {
-                mFakeStore.setContent(contentId,
-                        StreamPayload.newBuilder()
-                                .setStreamFeature(StreamFeature.getDefaultInstance())
-                                .build());
-            }
-        }
-        session.setSessionId("stream:" + id);
-        session.bindModelProvider(mFakeModelProvider, mock(ViewDepthProvider.class));
-        session.populateModelProvider(head, true, false, UiContext.getDefaultInstance());
-
-        if (commitToStore) {
-            mFakeStore.setStreamStructures(session.getSessionId(), head);
-        }
-
-        return session;
-    }
-
-    private void populateHead() {
-        mFakeStore.setStreamStructures(Store.HEAD_SESSION_ID,
-                StreamStructure.newBuilder()
-                        .setOperation(Operation.UPDATE_OR_APPEND)
-                        .setContentId(mIdGenerators.createRootContentId(1))
-                        .build());
-    }
-
-    private void setSessions(Session... testSessions) {
-        for (Session session : testSessions) {
-            mSessionCache.putAttached(
-                    session.getSessionId(), /* creationTimeMillis= */ 0L, SCHEMA_VERSION, session);
-        }
-    }
-
-    private void assertPayloads(int featureCnt) {
-        for (int i = 0; i < featureCnt; i++) {
-            String contentId = mIdGenerators.createFeatureContentId(i);
-            assertThat(mFakeStore.getContentById(contentId)).hasSize(1);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionContentTrackerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionContentTrackerTest.java
deleted file mode 100644
index fff6bda..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionContentTrackerTest.java
+++ /dev/null
@@ -1,122 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests of the {@link SessionContentTracker} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SessionContentTrackerTest {
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-
-    private InternalProtocolBuilder mProtocolBuilder;
-    private SessionContentTracker mSessionContentTracker;
-
-    @Before
-    public void setUp() {
-        mProtocolBuilder = new InternalProtocolBuilder();
-        mSessionContentTracker = new SessionContentTracker(/* supportsClearAll= */ true);
-    }
-
-    @Test
-    public void testUpdate_features() {
-        int featureCnt = 3;
-        addFeatures(featureCnt);
-        List<StreamStructure> streamStructures = mProtocolBuilder.buildAsStreamStructure();
-
-        // 3 features.
-        assertThat(streamStructures).hasSize(3);
-        mSessionContentTracker.update(streamStructures);
-        assertThat(mSessionContentTracker.getContentIds()).hasSize(featureCnt);
-        assertThat(mSessionContentTracker.contains(mContentIdGenerators.createFeatureContentId(1)))
-                .isTrue();
-    }
-
-    @Test
-    public void testUpdate_clearWithfeatures() {
-        int featureCnt = 3;
-        mProtocolBuilder.addClearOperation();
-        addFeatures(featureCnt);
-        List<StreamStructure> streamStructures = mProtocolBuilder.buildAsStreamStructure();
-
-        // 1 clear, 3 features.
-        assertThat(streamStructures).hasSize(4);
-        mSessionContentTracker.update(streamStructures);
-        assertThat(mSessionContentTracker.getContentIds()).hasSize(featureCnt);
-        assertThat(mSessionContentTracker.contains(mContentIdGenerators.createFeatureContentId(1)))
-                .isTrue();
-    }
-
-    @Test
-    public void testUpdate_featuresWithClear_enabled() {
-        int featureCnt = 3;
-        addFeatures(featureCnt);
-        mProtocolBuilder.addClearOperation();
-        List<StreamStructure> streamStructures = mProtocolBuilder.buildAsStreamStructure();
-
-        // 3 features, 1 clear.
-        assertThat(streamStructures).hasSize(4);
-        mSessionContentTracker.update(streamStructures);
-        assertThat(mSessionContentTracker.isEmpty()).isTrue();
-    }
-
-    @Test
-    public void testUpdate_featuresWithClear_disabled() {
-        mSessionContentTracker = new SessionContentTracker(/* supportsClearAll= */ false);
-
-        int featureCnt = 3;
-        addFeatures(featureCnt);
-        mProtocolBuilder.addClearOperation();
-        List<StreamStructure> streamStructures = mProtocolBuilder.buildAsStreamStructure();
-
-        // 3 features, 1 clear.
-        assertThat(streamStructures).hasSize(4);
-        mSessionContentTracker.update(streamStructures);
-        assertThat(mSessionContentTracker.isEmpty()).isFalse();
-    }
-
-    @Test
-    public void testUpdate_remove() {
-        int featureCnt = 2;
-        addFeatures(featureCnt);
-        mProtocolBuilder.removeFeature(mContentIdGenerators.createFeatureContentId(1),
-                mContentIdGenerators.createRootContentId(0));
-        List<StreamStructure> streamStructures = mProtocolBuilder.buildAsStreamStructure();
-
-        // 2 features, 1 remove.
-        assertThat(streamStructures).hasSize(3);
-        mSessionContentTracker.update(streamStructures);
-        assertThat(mSessionContentTracker.getContentIds()).hasSize(1);
-    }
-
-    @Test
-    public void testUpdate_requiredContent() {
-        String contentId = mContentIdGenerators.createFeatureContentId(1);
-        mProtocolBuilder.addRequiredContent(contentId);
-        mSessionContentTracker.update(mProtocolBuilder.buildAsStreamStructure());
-        assertThat(mSessionContentTracker.getContentIds()).hasSize(1);
-        assertThat(mSessionContentTracker.contains(contentId)).isTrue();
-    }
-
-    private void addFeatures(int featureCnt) {
-        for (int i = 0; i < featureCnt; i++) {
-            mProtocolBuilder.addFeature(mContentIdGenerators.createFeatureContentId(i + 1),
-                    mContentIdGenerators.createRootContentId(0));
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionImplTest.java
deleted file mode 100644
index 426c5be..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionImplTest.java
+++ /dev/null
@@ -1,133 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.testing.AbstractSessionImplTest;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelMutation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link SessionImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SessionImplTest extends AbstractSessionImplTest {
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-    private SessionImpl mSession;
-
-    @Before
-    @Override
-    public void setUp() {
-        super.setUp();
-        mFakeThreadUtils.enforceMainThread(false);
-        mSession = getSessionImpl();
-    }
-
-    @Test
-    public void testInvalidateOnResetHead() {
-        assertThat(mSession.invalidateOnResetHead()).isTrue();
-    }
-
-    @Test
-    public void testClearHead() {
-        mSession.setSessionId(TEST_SESSION_ID);
-        mSession.populateModelProvider(
-                new ArrayList<>(), false, false, UiContext.getDefaultInstance());
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // clear head will be ignored
-        assertThat(streamStructures).hasSize(4);
-        mSession.updateSession(true, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mFakeSessionMutation.streamStructures).isEmpty();
-        FakeModelMutation fakeModelMutation = mFakeModelProvider.getLatestModelMutation();
-        assertThat(fakeModelMutation.mAddedChildren).isEmpty();
-        assertThat(mSession.getContentInSession()).isEmpty();
-    }
-
-    @Test
-    public void testClearHead_paginationRequest() {
-        ViewDepthProvider mockDepthProvider = mock(ViewDepthProvider.class);
-        mSession.setSessionId(TEST_SESSION_ID);
-        mSession.bindModelProvider(mFakeModelProvider, mockDepthProvider);
-        mSession.populateModelProvider(
-                new ArrayList<>(), false, false, UiContext.getDefaultInstance());
-        int featureCnt = 3;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, 1);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-        mSession.updateSession(true, streamStructures, SCHEMA_VERSION,
-                new MutationContext.Builder()
-                        .setContinuationToken(
-                                StreamToken.newBuilder().setContentId("token").build())
-                        .setRequestingSessionId(TEST_SESSION_ID)
-                        .build());
-
-        assertThat(mFakeModelProvider.isInvalidated()).isTrue();
-    }
-
-    @Test
-    public void testModelProviderBinding_withDetach() {
-        ViewDepthProvider mockDepthProvider = mock(ViewDepthProvider.class);
-        mSession.bindModelProvider(mFakeModelProvider, mockDepthProvider);
-        assertThat(mSession.mModelProvider).isEqualTo(mFakeModelProvider);
-        assertThat(mSession.mViewDepthProvider).isEqualTo(mockDepthProvider);
-
-        mSession.bindModelProvider(null, null);
-        assertThat(mSession.mViewDepthProvider).isNull();
-        assertThat(mSession.mModelProvider).isNull();
-    }
-
-    @Test
-    public void testUpdateSession_requiredContent() {
-        String contentId = mContentIdGenerators.createFeatureContentId(1);
-        InternalProtocolBuilder protocolBuilder =
-                new InternalProtocolBuilder().addRequiredContent(contentId);
-        mSession.setSessionId(TEST_SESSION_ID);
-        mSession.updateSession(
-                /* clearHead= */ false, protocolBuilder.buildAsStreamStructure(), SCHEMA_VERSION,
-                /* mutationContext= */ null);
-
-        assertThat(mFakeSessionMutation.streamStructures).hasSize(1);
-        assertThat(mFakeModelProvider.getLatestModelMutation().mAddedChildren).isEmpty();
-        assertThat(mFakeModelProvider.getLatestModelMutation().mRemovedChildren).isEmpty();
-        assertThat(mFakeModelProvider.getLatestModelMutation().mUpdateChildren).isEmpty();
-        assertThat(mFakeModelProvider.getLatestModelMutation().isCommitted()).isTrue();
-    }
-
-    @Override
-    protected SessionImpl getSessionImpl() {
-        FakeTaskQueue fakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        fakeTaskQueue.initialize(() -> {});
-        SessionImpl session =
-                new SessionImpl(mStore, false, fakeTaskQueue, mTimingUtils, mFakeThreadUtils);
-        session.bindModelProvider(mFakeModelProvider, null);
-        return session;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionManagerMutationTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionManagerMutationTest.java
deleted file mode 100644
index 1ec16c2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionManagerMutationTest.java
+++ /dev/null
@@ -1,376 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.api.common.MutationContext.EMPTY_CONTEXT;
-
-import android.util.Pair;
-
-import com.google.common.collect.ImmutableList;
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.KnownContent;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.SessionManagerMutation.MutationCommitter;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.testing.store.FakeStore;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests of the {@link SessionManagerMutation} and the actual committer {@link
- * SessionManagerMutation.MutationCommitter}.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SessionManagerMutationTest {
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final ContentIdGenerators mIdGenerators = new ContentIdGenerators();
-    private final FakeBasicLoggingApi mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeMainThreadRunner mFakeMainThreadRunner =
-            FakeMainThreadRunner.runTasksImmediately();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final String mRootContentId = mIdGenerators.createRootContentId(0);
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    @Mock
-    private KnownContent.Listener mKnownContentListener;
-    @Mock
-    private SchedulerApi mSchedulerApi;
-    private ContentCache mContentCache;
-    private FakeTaskQueue mFakeTaskQueue;
-    private ModelError mNotifyError;
-    private Session mNotifySession;
-    private SessionCache mSessionCache;
-    private FakeStore mFakeStore;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mNotifySession = null;
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeTaskQueue.initialize(() -> {});
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeStore = new FakeStore(mConfiguration, mFakeThreadUtils, mFakeTaskQueue, mFakeClock);
-        SessionFactory sessionFactory = new SessionFactory(
-                mFakeStore, mFakeTaskQueue, mTimingUtils, mFakeThreadUtils, mConfiguration);
-        mSessionCache = new SessionCache(mFakeStore, mFakeTaskQueue, sessionFactory, 10L,
-                mTimingUtils, mFakeThreadUtils, mFakeClock);
-        mSessionCache.initialize();
-        mContentCache = new ContentCache();
-    }
-
-    @Test
-    public void testResultError() {
-        String sessionId = "session:1";
-        Session session = getSession(sessionId);
-
-        MutationContext mutationContext =
-                new MutationContext.Builder()
-                        .setContinuationToken(StreamToken.getDefaultInstance())
-                        .setRequestingSessionId(sessionId)
-                        .build();
-        MutationCommitter mutationCommitter = getMutationCommitter(mutationContext);
-        mutationCommitter.accept(Result.failure());
-        assertThat(mNotifySession).isEqualTo(session);
-        assertThat(mNotifyError.getErrorType()).isEqualTo(ErrorType.PAGINATION_ERROR);
-    }
-
-    @Test
-    public void testResultError_noSession() {
-        MutationCommitter mutationCommitter = getMutationCommitter(MutationContext.EMPTY_CONTEXT);
-        mutationCommitter.accept(Result.failure());
-        assertThat(mNotifySession).isEqualTo(null);
-        assertThat(mNotifyError.getErrorType()).isEqualTo(ErrorType.NO_CARDS_ERROR);
-    }
-
-    @Test
-    public void testResetHead() {
-        mFakeClock.set(5L);
-        List<StreamDataOperation> dataOperations = new ArrayList<>();
-        dataOperations.add(getStreamDataOperation(
-                StreamStructure.newBuilder().setOperation(Operation.CLEAR_ALL).build(), null));
-        String sessionId = "session:1";
-
-        MutationCommitter mutationCommitter = getMutationCommitter(
-                new MutationContext.Builder().setRequestingSessionId(sessionId).build());
-        mutationCommitter.accept(Result.success(Model.of(dataOperations)));
-        assertThat(mutationCommitter.mClearedHead).isTrue();
-        verify(mSchedulerApi).onReceiveNewContent(5L);
-        verify(mKnownContentListener).onNewContentReceived(true, 5L);
-    }
-
-    @Test
-    public void testUpdateHeadMetadata() {
-        int schemaVersion = 3;
-        long currentTime = 8L;
-        mFakeClock.set(currentTime);
-        MutationCommitter mutationCommitter = getMutationCommitter(MutationContext.EMPTY_CONTEXT);
-        mutationCommitter.accept(Result.success(Model.of(
-                ImmutableList.of(getStreamDataOperation(
-                        StreamStructure.newBuilder().setOperation(Operation.CLEAR_ALL).build(),
-                        /* payload= */ null)),
-                schemaVersion)));
-        assertThat(mSessionCache.getHead().getSchemaVersion()).isEqualTo(schemaVersion);
-        assertThat(mSessionCache.getHeadLastAddedTimeMillis()).isEqualTo(currentTime);
-    }
-
-    @Test
-    public void testUpdateContent() {
-        mFakeClock.set(8L);
-        List<String> contentIds = getContentIds(3);
-        List<Pair<StreamStructure, StreamPayload>> features = getFeatures(contentIds,
-                ()
-                        -> StreamPayload.newBuilder()
-                                   .setStreamFeature(StreamFeature.getDefaultInstance())
-                                   .build());
-        List<StreamDataOperation> dataOperations = new ArrayList<>();
-        for (Pair<StreamStructure, StreamPayload> feature : features) {
-            dataOperations.add(getStreamDataOperation(feature.first, feature.second));
-        }
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        mutationCommitter.accept(Result.success(Model.of(dataOperations)));
-
-        Result<List<PayloadWithId>> result = mFakeStore.getPayloads(contentIds);
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).hasSize(contentIds.size());
-        for (PayloadWithId payload : result.getValue()) {
-            assertThat(contentIds).contains(payload.contentId);
-        }
-        assertThat(mContentCache.size()).isEqualTo(0);
-        verify(mSchedulerApi, never()).onReceiveNewContent(anyLong());
-        verify(mKnownContentListener).onNewContentReceived(false, 8L);
-    }
-
-    @Test
-    public void testUpdateContent_failedCommitLogsError() {
-        mFakeStore.setAllowEditContent(false);
-
-        List<String> contentIds = getContentIds(3);
-        List<Pair<StreamStructure, StreamPayload>> features = getFeatures(contentIds,
-                ()
-                        -> StreamPayload.newBuilder()
-                                   .setStreamFeature(StreamFeature.getDefaultInstance())
-                                   .build());
-        List<StreamDataOperation> dataOperations = new ArrayList<>();
-        for (Pair<StreamStructure, StreamPayload> feature : features) {
-            dataOperations.add(getStreamDataOperation(feature.first, feature.second));
-        }
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        mutationCommitter.accept(Result.success(Model.of(dataOperations)));
-
-        assertThat(mFakeBasicLoggingApi.lastInternalError)
-                .isEqualTo(InternalFeedError.CONTENT_MUTATION_FAILED);
-    }
-
-    @Test
-    public void testSemanticData() {
-        List<String> contentIds = getContentIds(2);
-        List<Pair<StreamStructure, StreamPayload>> features = getFeatures(contentIds,
-                ()
-                        -> StreamPayload.newBuilder()
-                                   .setSemanticData(
-                                           ByteString.copyFrom("foo", Charset.defaultCharset()))
-                                   .build());
-        List<StreamDataOperation> dataOperations = new ArrayList<>();
-        for (Pair<StreamStructure, StreamPayload> feature : features) {
-            dataOperations.add(getStreamDataOperation(feature.first, feature.second));
-        }
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        mutationCommitter.accept(Result.success(Model.of(dataOperations)));
-
-        Result<List<SemanticPropertiesWithId>> result =
-                mFakeStore.getSemanticProperties(contentIds);
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).hasSize(contentIds.size());
-        for (SemanticPropertiesWithId payload : result.getValue()) {
-            assertThat(contentIds).contains(payload.contentId);
-        }
-    }
-
-    @Test
-    public void testValidDataOperation() {
-        StreamDataOperation operation = StreamDataOperation.getDefaultInstance();
-        assertThat(SessionManagerMutation.validDataOperation(operation)).isFalse();
-
-        operation = StreamDataOperation.newBuilder()
-                            .setStreamPayload(StreamPayload.getDefaultInstance())
-                            .build();
-        assertThat(SessionManagerMutation.validDataOperation(operation)).isFalse();
-
-        operation = StreamDataOperation.newBuilder()
-                            .setStreamStructure(StreamStructure.getDefaultInstance())
-                            .build();
-        assertThat(SessionManagerMutation.validDataOperation(operation)).isFalse();
-
-        operation = StreamDataOperation.newBuilder()
-                            .setStreamPayload(StreamPayload.getDefaultInstance())
-                            .setStreamStructure(StreamStructure.getDefaultInstance())
-                            .build();
-        assertThat(SessionManagerMutation.validDataOperation(operation)).isFalse();
-
-        operation = StreamDataOperation.newBuilder()
-                            .setStreamPayload(StreamPayload.getDefaultInstance())
-                            .setStreamStructure(
-                                    StreamStructure.newBuilder().setContentId("content").build())
-                            .build();
-        assertThat(SessionManagerMutation.validDataOperation(operation)).isTrue();
-    }
-
-    @Test
-    public void testInvalidateHead() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        mutationCommitter.resetHead(null);
-        assertThat(mFakeStore.getClearHeadCalled()).isTrue();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_modelProviderInitializing() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.INITIALIZING);
-        assertThat(mutationCommitter.shouldInvalidateSession(null, modelProvider)).isFalse();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_modelProviderInvalidated() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.INVALIDATED);
-        assertThat(mutationCommitter.shouldInvalidateSession(null, modelProvider)).isFalse();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_modelProviderReady() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.READY);
-        assertThat(mutationCommitter.shouldInvalidateSession(null, modelProvider)).isTrue();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_noModelProviderSession() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.READY);
-        assertThat(mutationCommitter.shouldInvalidateSession("session:2", modelProvider)).isTrue();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_differentSession() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        String sessionId = "session:1";
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.READY);
-        when(modelProvider.getSessionId()).thenReturn(sessionId);
-        assertThat(mutationCommitter.shouldInvalidateSession("session:2", modelProvider)).isFalse();
-    }
-
-    @Test
-    public void testShouldInvalidateSession_sameSession() {
-        MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
-        String sessionId = "session:1";
-        ModelProvider modelProvider = mock(ModelProvider.class);
-        when(modelProvider.getCurrentState()).thenReturn(State.READY);
-        when(modelProvider.getSessionId()).thenReturn(sessionId);
-        assertThat(mutationCommitter.shouldInvalidateSession(sessionId, modelProvider)).isTrue();
-    }
-
-    private MutationCommitter getMutationCommitter(MutationContext mutationContext) {
-        SessionManagerMutation mutation = new SessionManagerMutation(mFakeStore, mSessionCache,
-                mContentCache, mFakeTaskQueue, mSchedulerApi, mFakeThreadUtils, mTimingUtils,
-                mFakeClock, mFakeMainThreadRunner, mFakeBasicLoggingApi);
-        return (MutationCommitter) mutation.createCommitter(
-                "task", mutationContext, this::notifySessionError, mKnownContentListener);
-    }
-
-    private List<String> getContentIds(int count) {
-        List<String> contentIds = new ArrayList<>();
-        for (int i = 0; i < count; i++) {
-            contentIds.add(mIdGenerators.createFeatureContentId(i));
-        }
-        return contentIds;
-    }
-
-    private Session getSession(String sessionId) {
-        Session session = mock(Session.class);
-        when(session.getSessionId()).thenReturn(sessionId);
-        mSessionCache.putAttachedAndRetainMetadata(sessionId, session);
-        return session;
-    }
-
-    private List<Pair<StreamStructure, StreamPayload>> getFeatures(
-            List<String> contentIds, Supplier<StreamPayload> payloadConsumer) {
-        List<Pair<StreamStructure, StreamPayload>> values = new ArrayList<>();
-        for (String contentId : contentIds) {
-            StreamStructure streamStructure = StreamStructure.newBuilder()
-                                                      .setOperation(Operation.UPDATE_OR_APPEND)
-                                                      .setParentContentId(mRootContentId)
-                                                      .setContentId(contentId)
-                                                      .build();
-            StreamPayload streamPayload = payloadConsumer.get();
-            values.add(new Pair<>(streamStructure, streamPayload));
-        }
-        return values;
-    }
-
-    private StreamDataOperation getStreamDataOperation(
-            StreamStructure streamStructure, StreamPayload payload) {
-        StreamDataOperation.Builder builder =
-                StreamDataOperation.newBuilder().setStreamStructure(streamStructure);
-        if (payload != null) {
-            builder.setStreamPayload(payload);
-        }
-        return builder.build();
-    }
-
-    private void notifySessionError(Session session, ModelError error) {
-        mNotifySession = session;
-        mNotifyError = error;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/TimeoutSessionImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/TimeoutSessionImplTest.java
deleted file mode 100644
index 073d163a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/TimeoutSessionImplTest.java
+++ /dev/null
@@ -1,276 +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.
-
-package org.chromium.chrome.browser.feed.library.feedsessionmanager.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.InternalProtocolBuilder;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.internal.testing.AbstractSessionImplTest;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelChild;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelCursor;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelFeature;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelMutation;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link TimeoutSessionImpl} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TimeoutSessionImplTest extends AbstractSessionImplTest {
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    @Mock
-    private ViewDepthProvider mViewDepthProvider;
-
-    @Before
-    @Override
-    public void setUp() {
-        super.setUp();
-        mFakeThreadUtils.enforceMainThread(false);
-    }
-
-    @Test
-    public void testInvalidateOnResetHead() {
-        TimeoutSessionImpl session = getSessionImpl();
-        assertThat(session.invalidateOnResetHead()).isFalse();
-    }
-
-    @Test
-    public void testClearHead() {
-        SessionImpl session = getSessionImpl();
-        session.setSessionId(TEST_SESSION_ID);
-        session.populateModelProvider(
-                new ArrayList<>(), false, true, UiContext.getDefaultInstance());
-        int featureCnt = 3;
-        int featureId = 5;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, featureId);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        // 1 clear, 3 features
-        assertThat(streamStructures).hasSize(4);
-        session.updateSession(true, streamStructures, SCHEMA_VERSION, null);
-        assertThat(mFakeSessionMutation.streamStructures).hasSize(featureCnt);
-        FakeModelMutation fakeModelMutation = mFakeModelProvider.getLatestModelMutation();
-        assertThat(fakeModelMutation.mAddedChildren).hasSize(featureCnt);
-        assertThat(session.getContentInSession()).hasSize(featureCnt);
-        assertThat(session.getContentInSession())
-                .contains(mContentIdGenerators.createFeatureContentId(featureId));
-    }
-
-    @Test
-    public void testClearHead_onPaginationRequest() {
-        SessionImpl session = getSessionImpl();
-        session.setSessionId(TEST_SESSION_ID);
-        session.populateModelProvider(
-                new ArrayList<>(), false, true, UiContext.getDefaultInstance());
-        int featureCnt = 3;
-        int featureId = 5;
-        InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder().addClearOperation();
-        addFeatures(protocolBuilder, featureCnt, featureId);
-        List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
-
-        session.bindModelProvider(mFakeModelProvider, mViewDepthProvider);
-        session.populateModelProvider(
-                new ArrayList<>(), false, false, UiContext.getDefaultInstance());
-        session.updateSession(true, streamStructures, SCHEMA_VERSION,
-                new MutationContext.Builder()
-                        .setRequestingSessionId(TEST_SESSION_ID)
-                        .setContinuationToken(
-                                StreamToken.newBuilder().setContentId("token").build())
-                        .build());
-        assertThat(mFakeModelProvider.isInvalidated()).isTrue();
-    }
-
-    @Test
-    public void testPopulateModelProviderState() {
-        TimeoutSessionImpl session = getSessionImpl();
-        assertThat(session.mLegacyHeadContent).isFalse();
-        assertThat(session.mViewDepthProvider).isNull();
-
-        session.setSessionId(TEST_SESSION_ID);
-        session.bindModelProvider(mFakeModelProvider, mViewDepthProvider);
-        session.populateModelProvider(
-                new ArrayList<>(), false, true, UiContext.getDefaultInstance());
-        assertThat(session.mLegacyHeadContent).isTrue();
-        assertThat(session.mViewDepthProvider).isEqualTo(mViewDepthProvider);
-    }
-
-    @Test
-    public void testCreateRemoveFeature() {
-        TimeoutSessionImpl session = getSessionImpl();
-        String contentId = "contentId";
-        String parentContentId = "parentId";
-
-        StreamStructure streamStructure = session.createRemoveFeature(contentId, parentContentId);
-        assertThat(streamStructure.getContentId()).isEqualTo(contentId);
-        assertThat(streamStructure.getParentContentId()).isEqualTo(parentContentId);
-    }
-
-    @Test
-    public void testCreateRemoveFeature_nullParent() {
-        TimeoutSessionImpl session = getSessionImpl();
-        String contentId = "contentId";
-
-        StreamStructure streamStructure = session.createRemoveFeature(contentId, null);
-        assertThat(streamStructure.getContentId()).isEqualTo(contentId);
-        assertThat(streamStructure.getParentContentId()).isEmpty();
-    }
-
-    @Test
-    public void testCaptureRootContent() {
-        TimeoutSessionImpl session = getSessionImpl();
-        setupModelProviderRoot();
-
-        List<ModelChild> children = session.captureRootContent();
-        assertThat(children).isNotNull();
-        assertThat(children).hasSize(1);
-    }
-
-    @Test
-    public void testCaptureRootContent_nullRoot() {
-        TimeoutSessionImpl session = getSessionImpl();
-
-        List<ModelChild> children = session.captureRootContent();
-        assertThat(children).isEmpty();
-    }
-
-    @Test
-    public void testRemoveItems() {
-        TimeoutSessionImpl session = getSessionImpl();
-        List<ModelChild> modelChildren = new ArrayList<>();
-        for (int i = 0; i < 10; i++) {
-            modelChildren.add(createModelChild(i, ModelChild.Type.FEATURE));
-        }
-        setupForViewDepthProvider(
-                session, mContentIdGenerators.createFeatureContentId(6), modelChildren);
-
-        // Items below the lowest child should be removed.
-        session.updateSession(
-                /* clearHead= */ true, new ArrayList<>(), SCHEMA_VERSION,
-                /* mutationContext= */ null);
-        FakeModelMutation fakeModelMutation = mFakeModelProvider.getLatestModelMutation();
-        assertThat(fakeModelMutation.mRemovedChildren).hasSize(3);
-        assertThat(fakeModelMutation.mRemovedChildren.get(0).getContentId())
-                .isEqualTo(mContentIdGenerators.createFeatureContentId(7));
-        assertThat(fakeModelMutation.mRemovedChildren.get(1).getContentId())
-                .isEqualTo(mContentIdGenerators.createFeatureContentId(8));
-        assertThat(fakeModelMutation.mRemovedChildren.get(2).getContentId())
-                .isEqualTo(mContentIdGenerators.createFeatureContentId(9));
-    }
-
-    @Test
-    public void testRemoveItems_visibleTokenIsRemoved() {
-        TimeoutSessionImpl session = getSessionImpl();
-        List<ModelChild> modelChildren = new ArrayList<>();
-        modelChildren.add(createModelChild(0, ModelChild.Type.TOKEN));
-        String tokenContentId = mContentIdGenerators.createFeatureContentId(0);
-        setupForViewDepthProvider(session, tokenContentId, modelChildren);
-
-        // Because the lowest child is a token it should also be removed.
-        session.updateSession(
-                /* clearHead= */ true, new ArrayList<>(), SCHEMA_VERSION,
-                /* mutationContext= */ null);
-        FakeModelMutation fakeModelMutation = mFakeModelProvider.getLatestModelMutation();
-        assertThat(fakeModelMutation.mRemovedChildren).hasSize(1);
-        assertThat(fakeModelMutation.mRemovedChildren.get(0).getContentId())
-                .isEqualTo(tokenContentId);
-    }
-
-    @Test
-    public void testRemoveItems_nullLowestChildShouldRemoveToken() {
-        TimeoutSessionImpl session = getSessionImpl();
-        List<ModelChild> modelChildren = new ArrayList<>();
-        for (int i = 0; i < 10; i++) {
-            modelChildren.add(createModelChild(i, ModelChild.Type.FEATURE));
-        }
-        modelChildren.add(createModelChild(11, ModelChild.Type.TOKEN));
-        setupForViewDepthProvider(session, /* lowestChild= */ null, modelChildren);
-
-        // The token should be removed even when there is a null lowest child.
-        session.updateSession(
-                /* clearHead= */ true, new ArrayList<>(), SCHEMA_VERSION,
-                /* mutationContext= */ null);
-        FakeModelMutation fakeModelMutation = mFakeModelProvider.getLatestModelMutation();
-        assertThat(fakeModelMutation.mRemovedChildren).hasSize(1);
-        assertThat(fakeModelMutation.mRemovedChildren.get(0).getContentId())
-                .isEqualTo(mContentIdGenerators.createFeatureContentId(11));
-    }
-
-    private ModelChild createModelChild(long id, @ModelChild.Type int type) {
-        String contentId = mContentIdGenerators.createFeatureContentId(id);
-        switch (type) {
-            case ModelChild.Type.FEATURE:
-                return FakeModelChild.newBuilder()
-                        .setContentId(contentId)
-                        .setModelFeature(FakeModelFeature.newBuilder().build())
-                        .build();
-            case ModelChild.Type.TOKEN:
-                return FakeModelChild.newBuilder()
-                        .setContentId(contentId)
-                        .setModelToken(FakeModelToken.newBuilder().build())
-                        .build();
-            default:
-                return FakeModelChild.newBuilder().setContentId(contentId).build();
-        }
-    }
-
-    private void setupModelProviderRoot() {
-        mFakeModelProvider.triggerOnSessionStart(
-                FakeModelFeature.newBuilder()
-                        .setModelCursor(FakeModelCursor.newBuilder()
-                                                .addChild(FakeModelChild.newBuilder().build())
-                                                .build())
-                        .build());
-    }
-
-    @Override
-    protected TimeoutSessionImpl getSessionImpl() {
-        FakeTaskQueue fakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        fakeTaskQueue.initialize(() -> {});
-        TimeoutSessionImpl session = new TimeoutSessionImpl(
-                mStore, false, fakeTaskQueue, mTimingUtils, mFakeThreadUtils);
-        session.bindModelProvider(mFakeModelProvider, /* viewDepthProvider= */ null);
-        return session;
-    }
-
-    private void setupForViewDepthProvider(
-            TimeoutSessionImpl session, String lowestChild, List<ModelChild> modelChildren) {
-        when(mViewDepthProvider.getChildViewDepth()).thenReturn(lowestChild);
-        mFakeModelProvider.triggerOnSessionStart(
-                FakeModelFeature.newBuilder()
-                        .setModelCursor(
-                                FakeModelCursor.newBuilder().addChildren(modelChildren).build())
-                        .build());
-        session.setSessionId(TEST_SESSION_ID);
-        session.bindModelProvider(mFakeModelProvider, mViewDepthProvider);
-        session.populateModelProvider(new ArrayList<>(),
-                /* cachedBindings= */ false,
-                /* legacyHeadContent= */ true, UiContext.getDefaultInstance());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreEphemeralModeTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreEphemeralModeTest.java
deleted file mode 100644
index e5681067..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreEphemeralModeTest.java
+++ /dev/null
@@ -1,62 +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.
-
-package org.chromium.chrome.browser.feed.library.feedstore;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.AbstractFeedStoreTest;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryContentStorage;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryJournalStorage;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-
-/** Tests of the {@link FeedStore} class when running in ephemeral mode */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedStoreEphemeralModeTest extends AbstractFeedStoreTest {
-    private final FakeBasicLoggingApi mFakeBasicLoggingApi = new FakeBasicLoggingApi();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final FeedExtensionRegistry mExtensionRegistry =
-            new FeedExtensionRegistry(ArrayList::new);
-    private final ContentStorageDirect mContentStorage = new InMemoryContentStorage();
-
-    @Mock
-    private Configuration mConfiguration;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        when(mConfiguration.getValueOrDefault(ConfigKey.USE_DIRECT_STORAGE, false))
-                .thenReturn(false);
-    }
-
-    @Override
-    protected Store getStore(MainThreadRunner mainThreadRunner) {
-        FakeTaskQueue fakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        fakeTaskQueue.initialize(() -> {});
-        FeedStore feedStore = new FeedStore(mConfiguration, mTimingUtils, mExtensionRegistry,
-                mContentStorage, new InMemoryJournalStorage(), mFakeThreadUtils, fakeTaskQueue,
-                mFakeClock, mFakeBasicLoggingApi, mainThreadRunner);
-        mFakeThreadUtils.enforceMainThread(false);
-        feedStore.switchToEphemeralMode();
-        return feedStore;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreTest.java
deleted file mode 100644
index 36a9a722..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreTest.java
+++ /dev/null
@@ -1,195 +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.
-
-package org.chromium.chrome.browser.feed.library.feedstore;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.DISMISS_ACTION_JOURNAL;
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.SEMANTIC_PROPERTIES_PREFIX;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentOperation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentOperation.Upsert;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalOperation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalOperation.Append;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.api.internal.store.StoreListener;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.feedapplifecyclelistener.FeedLifecycleListener.LifecycleEvent;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.AbstractFeedStoreTest;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingContentStorage;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingJournalStorage;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryContentStorage;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryJournalStorage;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLocalAction;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-
-/** Tests of the {@link FeedStore} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FeedStoreTest extends AbstractFeedStoreTest {
-    private static final String CONTENT_ID = "contentId";
-    private static final StreamStructure STREAM_STRUCTURE =
-            StreamStructure.newBuilder()
-                    .setContentId(CONTENT_ID)
-                    .setOperation(Operation.UPDATE_OR_APPEND)
-                    .build();
-    private static final StreamPayload PAYLOAD =
-            StreamPayload.newBuilder()
-                    .setStreamFeature(StreamFeature.newBuilder().setContentId(CONTENT_ID))
-                    .build();
-    private static final byte[] SEMANTIC_PROPERTIES = new byte[] {4, 12, 18, 5};
-
-    private final ContentStorageDirect mContentStorage = new InMemoryContentStorage();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final FeedExtensionRegistry mExtensionRegistry =
-            new FeedExtensionRegistry(ArrayList::new);
-
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    @Mock
-    private Configuration mConfiguration;
-    @Mock
-    private StoreListener mListener;
-    private FakeMainThreadRunner mMainThreadRunner;
-    private FakeTaskQueue mTaskQueue;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        when(mConfiguration.getValueOrDefault(ConfigKey.USE_DIRECT_STORAGE, false))
-                .thenReturn(false);
-        mTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mTaskQueue.initialize(() -> {});
-        mMainThreadRunner = FakeMainThreadRunner.runTasksImmediately();
-        mFakeThreadUtils.enforceMainThread(false);
-    }
-
-    @Override
-    protected Store getStore(MainThreadRunner mainThreadRunner) {
-        return new FeedStore(mConfiguration, mTimingUtils, mExtensionRegistry, mContentStorage,
-                new InMemoryJournalStorage(), mFakeThreadUtils, mTaskQueue, mFakeClock,
-                mBasicLoggingApi, this.mMainThreadRunner);
-    }
-
-    @Test
-    public void testSwitchToEphemeralMode() {
-        FeedStore store = (FeedStore) getStore(mMainThreadRunner);
-        assertThat(store.isEphemeralMode()).isFalse();
-        store.switchToEphemeralMode();
-        assertThat(store.isEphemeralMode()).isTrue();
-        verify(mBasicLoggingApi).onInternalError(InternalFeedError.SWITCH_TO_EPHEMERAL);
-    }
-
-    @Test
-    public void testSwitchToEphemeralMode_listeners() {
-        FeedStore store = (FeedStore) getStore(mMainThreadRunner);
-        assertThat(store.isEphemeralMode()).isFalse();
-
-        store.registerObserver(mListener);
-
-        store.switchToEphemeralMode();
-        assertThat(store.isEphemeralMode()).isTrue();
-        verify(mListener).onSwitchToEphemeralMode();
-    }
-
-    @Test
-    public void testDumpEphemeralActions_notEphemeralMode() {
-        JournalStorageDirect journalStorageSpy =
-                spy(new DelegatingJournalStorage(new InMemoryJournalStorage()));
-        ContentStorageDirect contentStorageSpy =
-                spy(new DelegatingContentStorage(this.mContentStorage));
-        FeedStore store = new FeedStore(mConfiguration, mTimingUtils, mExtensionRegistry,
-                contentStorageSpy, journalStorageSpy, mFakeThreadUtils, mTaskQueue, mFakeClock,
-                mBasicLoggingApi, mMainThreadRunner);
-        store.onLifecycleEvent(LifecycleEvent.ENTER_BACKGROUND);
-        verifyZeroInteractions(journalStorageSpy, contentStorageSpy);
-    }
-
-    @Test
-    public void testDumpEphemeralActions_ephemeralMode() throws InvalidProtocolBufferException {
-        JournalStorageDirect journalStorageSpy =
-                spy(new DelegatingJournalStorage(new InMemoryJournalStorage()));
-        ContentStorageDirect contentStorageSpy =
-                spy(new DelegatingContentStorage(this.mContentStorage));
-        FeedStore store = new FeedStore(mConfiguration, mTimingUtils, mExtensionRegistry,
-                contentStorageSpy, journalStorageSpy, mFakeThreadUtils, mTaskQueue, mFakeClock,
-                mBasicLoggingApi, mMainThreadRunner);
-        store.switchToEphemeralMode();
-        reset(journalStorageSpy, contentStorageSpy);
-
-        // Add ephemeral semantic properties, content, and actions
-        store.editSemanticProperties()
-                .add(CONTENT_ID, ByteString.copyFrom(SEMANTIC_PROPERTIES))
-                .commit();
-        store.editLocalActions().add(ActionType.DISMISS, CONTENT_ID).commit();
-        store.editContent().add(CONTENT_ID, PAYLOAD).commit();
-        store.editSession(Store.HEAD_SESSION_ID).add(STREAM_STRUCTURE).commit();
-
-        store.onLifecycleEvent(LifecycleEvent.ENTER_BACKGROUND);
-
-        // Verify content is written for semantic properties and actions only
-        ArgumentCaptor<JournalMutation> journalMutationArgumentCaptor =
-                ArgumentCaptor.forClass(JournalMutation.class);
-        verify(journalStorageSpy).commit(journalMutationArgumentCaptor.capture());
-
-        JournalMutation journalMutation = journalMutationArgumentCaptor.getValue();
-        assertThat(journalMutation.getJournalName()).isEqualTo(DISMISS_ACTION_JOURNAL);
-        assertThat(journalMutation.getOperations()).hasSize(1);
-        assertThat(journalMutation.getOperations().get(0).getType())
-                .isEqualTo(JournalOperation.Type.APPEND);
-        byte[] journalMutationBytes = ((Append) journalMutation.getOperations().get(0)).getValue();
-        StreamLocalAction action = StreamLocalAction.parseFrom(journalMutationBytes);
-        assertThat(action.getAction()).isEqualTo(ActionType.DISMISS);
-        assertThat(action.getFeatureContentId()).isEqualTo(CONTENT_ID);
-
-        ArgumentCaptor<ContentMutation> contentMutationArgumentCaptor =
-                ArgumentCaptor.forClass(ContentMutation.class);
-        verify(contentStorageSpy).commit(contentMutationArgumentCaptor.capture());
-
-        ContentMutation contentMutation = contentMutationArgumentCaptor.getValue();
-        assertThat(contentMutation.getOperations()).hasSize(1);
-        assertThat(contentMutation.getOperations().get(0).getType())
-                .isEqualTo(ContentOperation.Type.UPSERT);
-        Upsert upsert = (Upsert) contentMutation.getOperations().get(0);
-        assertThat(upsert.getKey()).isEqualTo(SEMANTIC_PROPERTIES_PREFIX + CONTENT_ID);
-        assertThat(upsert.getValue()).isEqualTo(SEMANTIC_PROPERTIES);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/ContentGcTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/ContentGcTest.java
deleted file mode 100644
index 63346fd..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/ContentGcTest.java
+++ /dev/null
@@ -1,334 +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.
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.SEMANTIC_PROPERTIES_PREFIX;
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.SHARED_STATE_PREFIX;
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.UPLOADABLE_ACTION_PREFIX;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.Task;
-import org.chromium.chrome.browser.feed.library.api.host.storage.CommitResult;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentOperation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue.TaskType;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeDirectExecutor;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLocalAction;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests of the {@link ContentGc} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentGcTest {
-    @Captor
-    private ArgumentCaptor<ContentMutation> mContentMutationCaptor;
-    @Mock
-    private ContentStorageDirect mContentStorage;
-
-    private static final String CONTENT_ID_1 = "contentId1";
-    private static final String CONTENT_ID_2 = "contentId2";
-    private static final String SEMANTIC_PROPERTIES_1 = SEMANTIC_PROPERTIES_PREFIX + CONTENT_ID_1;
-    private static final String SEMANTIC_PROPERTIES_2 = SEMANTIC_PROPERTIES_PREFIX + CONTENT_ID_2;
-    private static final String ACTION_1 = UPLOADABLE_ACTION_PREFIX + CONTENT_ID_1;
-    private static final long MAXIMUM_GC_ATTEMPTS = 3L;
-
-    private final Configuration mConfiguration =
-            new Configuration.Builder()
-                    .put(ConfigKey.MAXIMUM_GC_ATTEMPTS, MAXIMUM_GC_ATTEMPTS)
-                    .build();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final FakeDirectExecutor mFakeDirectExecutor =
-            FakeDirectExecutor.queueAllTasks(mFakeThreadUtils);
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    private FakeTaskQueue mFakeTaskQueue;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeDirectExecutor);
-        mFakeTaskQueue.initialize(() -> {});
-    }
-
-    @Test
-    public void gc() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations = new ContentMutation.Builder()
-                                                            .delete(CONTENT_ID_1)
-                                                            .delete(CONTENT_ID_2)
-                                                            .build()
-                                                            .getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_accessible() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration,
-                ()
-                        -> ImmutableSet.of(CONTENT_ID_1),
-                ImmutableSet.of(), ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(CONTENT_ID_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_reserved() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc =
-                new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(CONTENT_ID_1),
-                        ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                        /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(CONTENT_ID_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_actionUploads_validAction() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(ACTION_1);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_semanticProperties_noAction() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(SEMANTIC_PROPERTIES_1);
-        contentKeys.add(SEMANTIC_PROPERTIES_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations = new ContentMutation.Builder()
-                                                            .delete(SEMANTIC_PROPERTIES_1)
-                                                            .delete(SEMANTIC_PROPERTIES_2)
-                                                            .build()
-                                                            .getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_semanticProperties_validAction() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(SEMANTIC_PROPERTIES_1);
-        contentKeys.add(SEMANTIC_PROPERTIES_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ()
-                        -> ImmutableSet.of(StreamLocalAction.newBuilder()
-                                                   .setAction(ActionType.DISMISS)
-                                                   .setFeatureContentId(CONTENT_ID_1)
-                                                   .build()),
-                mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(SEMANTIC_PROPERTIES_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_semanticProperties_accessibleContent() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(CONTENT_ID_1);
-        contentKeys.add(SEMANTIC_PROPERTIES_1);
-        contentKeys.add(SEMANTIC_PROPERTIES_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration,
-                ()
-                        -> ImmutableSet.of(CONTENT_ID_1),
-                ImmutableSet.of(), ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(SEMANTIC_PROPERTIES_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_prefixed_sharedState() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(SHARED_STATE_PREFIX + CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ true);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(CONTENT_ID_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_deleteSharedState() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(SHARED_STATE_PREFIX + CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ false);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder()
-                        .delete(SHARED_STATE_PREFIX + CONTENT_ID_1)
-                        .delete(CONTENT_ID_2)
-                        .build()
-                        .getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_keepAccessibleSharedState() {
-        List<String> contentKeys = new ArrayList<>();
-        contentKeys.add(SHARED_STATE_PREFIX + CONTENT_ID_1);
-        contentKeys.add(CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration,
-                ()
-                        -> ImmutableSet.of(CONTENT_ID_1),
-                ImmutableSet.of(), ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ false);
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-        List<ContentOperation> expectedOperations =
-                new ContentMutation.Builder().delete(CONTENT_ID_2).build().getOperations();
-        List<ContentOperation> resultOperations = mContentMutationCaptor.getValue().getOperations();
-        assertListsContainSameElements(expectedOperations, resultOperations);
-    }
-
-    @Test
-    public void gc_delayWhileTaskEnqueued() {
-        mFakeTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, () -> {});
-        List<String> contentKeys = ImmutableList.of(CONTENT_ID_1, CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ false);
-        contentGc.gc();
-
-        assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(2);
-        verify(mContentStorage, never()).commit(mContentMutationCaptor.capture());
-    }
-
-    @Test
-    public void gc_runWhenMaximumAttemptsReached() {
-        mFakeTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, () -> {});
-        List<String> contentKeys = ImmutableList.of(CONTENT_ID_1, CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(mConfiguration, ImmutableSet::of, ImmutableSet.of(),
-                ImmutableSet::of, mContentStorage, mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ false);
-        for (int i = 0; i < MAXIMUM_GC_ATTEMPTS; i++) {
-            contentGc.gc();
-            assertThat(mFakeTaskQueue.getBackgroundTaskCount()).isEqualTo(2 + i);
-            verify(mContentStorage, never()).commit(mContentMutationCaptor.capture());
-        }
-
-        contentGc.gc();
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-    }
-
-    @Test
-    public void gc_runWhenMaximumIsZero() {
-        mFakeTaskQueue.execute(Task.UNKNOWN, TaskType.BACKGROUND, () -> {});
-        List<String> contentKeys = ImmutableList.of(CONTENT_ID_1, CONTENT_ID_2);
-        mockContentStorageWithContents(contentKeys);
-        ContentGc contentGc = new ContentGc(
-                new Configuration.Builder().put(ConfigKey.MAXIMUM_GC_ATTEMPTS, 0L).build(),
-                ImmutableSet::of, ImmutableSet.of(), ImmutableSet::of, mContentStorage,
-                mFakeTaskQueue, mTimingUtils,
-                /* keepSharedStates= */ false);
-        contentGc.gc();
-
-        verify(mContentStorage).commit(mContentMutationCaptor.capture());
-    }
-
-    private void mockContentStorageWithContents(List<String> contentKeys) {
-        when(mContentStorage.getAllKeys()).thenReturn(Result.success(contentKeys));
-        when(mContentStorage.commit(any(ContentMutation.class))).thenReturn(CommitResult.SUCCESS);
-    }
-
-    private void assertListsContainSameElements(
-            List<ContentOperation> expectedOperations, List<ContentOperation> resultOperations) {
-        assertThat(resultOperations.size()).isEqualTo(expectedOperations.size());
-        assertThat(resultOperations.containsAll(expectedOperations)).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/EphemeralFeedStoreTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/EphemeralFeedStoreTest.java
deleted file mode 100644
index 7e9bf6c..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/EphemeralFeedStoreTest.java
+++ /dev/null
@@ -1,30 +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.
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.AbstractClearableFeedStoreTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link EphemeralFeedStore} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class EphemeralFeedStoreTest extends AbstractClearableFeedStoreTest {
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-    }
-
-    @Override
-    protected Store getStore(MainThreadRunner mainThreadRunner) {
-        return new EphemeralFeedStore(mFakeClock, mTimingUtils, new FeedStoreHelper());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/LocalActionGcTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/LocalActionGcTest.java
deleted file mode 100644
index dacf9c1..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/LocalActionGcTest.java
+++ /dev/null
@@ -1,170 +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.
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalOperation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalOperation.Append;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalOperation.Type;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLocalAction;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Tests of the {@link LocalActionGc} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LocalActionGcTest {
-    private static final String DISMISS_JOURNAL_NAME = "DISMISS";
-    private static final String CONTENT_ID_1 = "contentId1";
-    private static final String CONTENT_ID_2 = "contentId2";
-
-    @Mock
-    private JournalStorageDirect mJournalStorage;
-    private TimingUtils mTimingUtils = new TimingUtils();
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-    }
-
-    @Test
-    public void gc_empty() throws Exception {
-        // Probably won't be called with empty actions
-        List<StreamLocalAction> allActions = Collections.emptyList();
-        List<String> validContentIds = Collections.emptyList();
-
-        ArgumentCaptor<JournalMutation> journalMutationCaptor =
-                ArgumentCaptor.forClass(JournalMutation.class);
-
-        LocalActionGc localActionGc = new LocalActionGc(
-                allActions, validContentIds, mJournalStorage, mTimingUtils, DISMISS_JOURNAL_NAME);
-        localActionGc.gc();
-
-        verify(mJournalStorage).commit(journalMutationCaptor.capture());
-
-        JournalMutation journalMutation = journalMutationCaptor.getValue();
-        assertThat(journalMutation.getJournalName()).isEqualTo(DISMISS_JOURNAL_NAME);
-        List<JournalOperation> journalOperations = journalMutation.getOperations();
-        assertThat(journalOperations).hasSize(1);
-        assertThat(journalOperations.get(0).getType()).isEqualTo(Type.DELETE);
-    }
-
-    @Test
-    public void gc_allValid() throws Exception {
-        StreamLocalAction action1 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_1)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(43))
-                                            .build();
-        StreamLocalAction action2 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_2)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(44))
-                                            .build();
-        List<StreamLocalAction> allActions = Arrays.asList(action1, action2);
-        List<String> validContentIds = Arrays.asList(CONTENT_ID_1, CONTENT_ID_2);
-
-        ArgumentCaptor<JournalMutation> journalMutationCaptor =
-                ArgumentCaptor.forClass(JournalMutation.class);
-
-        LocalActionGc localActionGc = new LocalActionGc(
-                allActions, validContentIds, mJournalStorage, mTimingUtils, DISMISS_JOURNAL_NAME);
-        localActionGc.gc();
-
-        verify(mJournalStorage).commit(journalMutationCaptor.capture());
-
-        JournalMutation journalMutation = journalMutationCaptor.getValue();
-        assertThat(journalMutation.getJournalName()).isEqualTo(DISMISS_JOURNAL_NAME);
-        List<JournalOperation> journalOperations = journalMutation.getOperations();
-        assertThat(journalOperations).hasSize(3);
-        assertThat(journalOperations.get(0).getType()).isEqualTo(Type.DELETE);
-        assertThat(journalOperations.get(1).getType()).isEqualTo(Type.APPEND);
-        assertThat(((Append) journalOperations.get(1)).getValue()).isEqualTo(action1.toByteArray());
-        assertThat(journalOperations.get(2).getType()).isEqualTo(Type.APPEND);
-        assertThat(((Append) journalOperations.get(2)).getValue()).isEqualTo(action2.toByteArray());
-    }
-
-    @Test
-    public void gc_allInvalid() throws Exception {
-        StreamLocalAction action1 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_1)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(43))
-                                            .build();
-        StreamLocalAction action2 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_2)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(44))
-                                            .build();
-        List<StreamLocalAction> allActions = Arrays.asList(action1, action2);
-        List<String> validContentIds = Collections.emptyList();
-
-        ArgumentCaptor<JournalMutation> journalMutationCaptor =
-                ArgumentCaptor.forClass(JournalMutation.class);
-
-        LocalActionGc localActionGc = new LocalActionGc(
-                allActions, validContentIds, mJournalStorage, mTimingUtils, DISMISS_JOURNAL_NAME);
-        localActionGc.gc();
-
-        verify(mJournalStorage).commit(journalMutationCaptor.capture());
-
-        JournalMutation journalMutation = journalMutationCaptor.getValue();
-        assertThat(journalMutation.getJournalName()).isEqualTo(DISMISS_JOURNAL_NAME);
-        List<JournalOperation> journalOperations = journalMutation.getOperations();
-        assertThat(journalOperations).hasSize(1);
-        assertThat(journalOperations.get(0).getType()).isEqualTo(Type.DELETE);
-    }
-
-    @Test
-    public void gc_someValid() throws Exception {
-        StreamLocalAction action1 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_1)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(43))
-                                            .build();
-        StreamLocalAction action2 = StreamLocalAction.newBuilder()
-                                            .setAction(ActionType.DISMISS)
-                                            .setFeatureContentId(CONTENT_ID_2)
-                                            .setTimestampSeconds(TimeUnit.DAYS.toSeconds(44))
-                                            .build();
-        List<StreamLocalAction> allActions = Arrays.asList(action1, action2);
-        List<String> validContentIds = Collections.singletonList(CONTENT_ID_2);
-
-        ArgumentCaptor<JournalMutation> journalMutationCaptor =
-                ArgumentCaptor.forClass(JournalMutation.class);
-
-        LocalActionGc localActionGc = new LocalActionGc(
-                allActions, validContentIds, mJournalStorage, mTimingUtils, DISMISS_JOURNAL_NAME);
-        localActionGc.gc();
-
-        verify(mJournalStorage).commit(journalMutationCaptor.capture());
-
-        JournalMutation journalMutation = journalMutationCaptor.getValue();
-        assertThat(journalMutation.getJournalName()).isEqualTo(DISMISS_JOURNAL_NAME);
-        List<JournalOperation> journalOperations = journalMutation.getOperations();
-        assertThat(journalOperations).hasSize(2);
-        assertThat(journalOperations.get(0).getType()).isEqualTo(Type.DELETE);
-        assertThat(journalOperations.get(1).getType()).isEqualTo(Type.APPEND);
-        assertThat(((Append) journalOperations.get(1)).getValue()).isEqualTo(action2.toByteArray());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/PersistentFeedStoreTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/PersistentFeedStoreTest.java
deleted file mode 100644
index 7fcdeb9..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/PersistentFeedStoreTest.java
+++ /dev/null
@@ -1,463 +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.
-
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.spy;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.feedstore.internal.FeedStoreConstants.SHARED_STATE_PREFIX;
-
-import com.google.common.collect.ImmutableList;
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.logging.InternalFeedError;
-import org.chromium.chrome.browser.feed.library.api.host.storage.CommitResult;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.ContentStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalMutation;
-import org.chromium.chrome.browser.feed.library.api.host.storage.JournalStorageDirect;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.store.LocalActionMutation.ActionType;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.AbstractClearableFeedStoreTest;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingContentStorage;
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingJournalStorage;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryContentStorage;
-import org.chromium.chrome.browser.feed.library.hostimpl.storage.testing.InMemoryJournalStorage;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLocalAction;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamUploadableAction;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Tests of the {@link
- * org.chromium.chrome.browser.feed.library.feedstore.internal.PersistentFeedStore} class.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PersistentFeedStoreTest extends AbstractClearableFeedStoreTest {
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final FeedExtensionRegistry mExtensionRegistry =
-            new FeedExtensionRegistry(ArrayList::new);
-    private final ContentStorageDirect mContentStorage = new InMemoryContentStorage();
-    private final JournalStorageDirect mJournalStorage = new InMemoryJournalStorage();
-    private final FakeBasicLoggingApi mBasicLoggingApi = new FakeBasicLoggingApi();
-    private final FakeMainThreadRunner mMainThreadRunner =
-            FakeMainThreadRunner.runTasksImmediately();
-
-    private FakeTaskQueue mFakeTaskQueue;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mFakeTaskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        mFakeTaskQueue.initialize(() -> {});
-        mFakeThreadUtils.enforceMainThread(false);
-    }
-
-    @Override
-    protected Store getStore(MainThreadRunner mainThreadRunner) {
-        return new PersistentFeedStore(Configuration.getDefaultInstance(), mTimingUtils,
-                mExtensionRegistry, mContentStorage, mJournalStorage, mFakeTaskQueue,
-                mFakeThreadUtils, mFakeClock, new FeedStoreHelper(), mBasicLoggingApi,
-                mainThreadRunner);
-    }
-
-    @Test
-    public void clearStorage_contentStorage_failure_getAllContent() {
-        ContentStorageDirect contentStorageSpy = spy(new DelegatingContentStorage(mContentStorage));
-        PersistentFeedStore store = new PersistentFeedStore(Configuration.getDefaultInstance(),
-                mTimingUtils, mExtensionRegistry, contentStorageSpy, mJournalStorage,
-                mFakeTaskQueue, mFakeThreadUtils, mFakeClock, new FeedStoreHelper(),
-                mBasicLoggingApi, mMainThreadRunner);
-        doAnswer(ans -> Result.failure()).when(contentStorageSpy).getAllKeys();
-
-        boolean clearSuccess = store.clearNonActionContent();
-        assertThat(clearSuccess).isFalse();
-    }
-
-    @Test
-    public void clearStorage_contentStorage_failure_commit() {
-        ContentStorageDirect contentStorageSpy = spy(new DelegatingContentStorage(mContentStorage));
-        PersistentFeedStore store = new PersistentFeedStore(Configuration.getDefaultInstance(),
-                mTimingUtils, mExtensionRegistry, contentStorageSpy, mJournalStorage,
-                mFakeTaskQueue, mFakeThreadUtils, mFakeClock, new FeedStoreHelper(),
-                mBasicLoggingApi, mMainThreadRunner);
-
-        CommitResult commitResult = store.editContent().add(CONTENT_ID, STREAM_PAYLOAD).commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<List<PayloadWithId>> payloadsResult =
-                store.getPayloads(Collections.singletonList(CONTENT_ID));
-        assertThat(payloadsResult.isSuccessful()).isTrue();
-        assertThat(payloadsResult.getValue()).hasSize(1);
-        assertThat(payloadsResult.getValue().get(0).contentId).isEqualTo(CONTENT_ID);
-        assertThat(payloadsResult.getValue().get(0).payload).isEqualTo(STREAM_PAYLOAD);
-        doAnswer(ans -> CommitResult.FAILURE)
-                .when(contentStorageSpy)
-                .commit(any(ContentMutation.class));
-
-        boolean clearSuccess = store.clearNonActionContent();
-        assertThat(clearSuccess).isFalse();
-    }
-
-    @Test
-    public void clearStorage_journalStorage_failure_getAllJournals() {
-        JournalStorageDirect journalStorageSpy = spy(new DelegatingJournalStorage(mJournalStorage));
-        PersistentFeedStore store = new PersistentFeedStore(Configuration.getDefaultInstance(),
-                mTimingUtils, mExtensionRegistry, mContentStorage, journalStorageSpy,
-                mFakeTaskQueue, mFakeThreadUtils, mFakeClock, new FeedStoreHelper(),
-                mBasicLoggingApi, mMainThreadRunner);
-
-        boolean commitResult =
-                store.editSession(SESSION_ID)
-                        .add(StreamStructure.newBuilder().setContentId(CONTENT_ID).build())
-                        .commit();
-        assertThat(commitResult).isTrue();
-
-        Result<List<String>> sessionsResult = store.getAllSessions();
-        assertThat(sessionsResult.isSuccessful()).isTrue();
-        assertThat(sessionsResult.getValue()).hasSize(1);
-        assertThat(sessionsResult.getValue().get(0)).isEqualTo(SESSION_ID);
-
-        doAnswer(ans -> CommitResult.FAILURE)
-                .when(journalStorageSpy)
-                .commit(any(JournalMutation.class));
-
-        boolean clearSuccess = store.clearNonActionContent();
-        assertThat(clearSuccess).isFalse();
-    }
-
-    @Test
-    public void clearStorage_journalStorage_failure_deleteJournal() {
-        JournalStorageDirect journalStorageSpy = spy(new DelegatingJournalStorage(mJournalStorage));
-        PersistentFeedStore store = new PersistentFeedStore(Configuration.getDefaultInstance(),
-                mTimingUtils, mExtensionRegistry, mContentStorage, journalStorageSpy,
-                mFakeTaskQueue, mFakeThreadUtils, mFakeClock, new FeedStoreHelper(),
-                mBasicLoggingApi, mMainThreadRunner);
-        doAnswer(ans -> Result.failure()).when(journalStorageSpy).getAllJournals();
-
-        boolean clearSuccess = store.clearNonActionContent();
-        assertThat(clearSuccess).isFalse();
-    }
-
-    @Test
-    public void clearStorage_allStorage() {
-        PersistentFeedStore store = (PersistentFeedStore) getStore(mMainThreadRunner);
-
-        /*
-        SETUP
-        */
-
-        // Payload
-        CommitResult commitResult = store.editContent()
-                                            .add(CONTENT_ID, STREAM_PAYLOAD)
-                                            .add(CONTENT_ID_2, STREAM_PAYLOAD_2)
-                                            .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<List<PayloadWithId>> payloadsResult =
-                store.getPayloads(Arrays.asList(CONTENT_ID, CONTENT_ID_2));
-        assertThat(payloadsResult.isSuccessful()).isTrue();
-        assertThat(payloadsResult.getValue()).hasSize(2);
-
-        // Semantic properties
-        commitResult = store.editSemanticProperties()
-                               .add(CONTENT_ID, ByteString.copyFrom(SEMANTIC_PROPERTIES))
-                               .add(CONTENT_ID_2, ByteString.copyFrom(SEMANTIC_PROPERTIES_2))
-                               .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<List<SemanticPropertiesWithId>> semanticPropertiesResult =
-                store.getSemanticProperties(Arrays.asList(CONTENT_ID, CONTENT_ID_2));
-        assertThat(semanticPropertiesResult.isSuccessful()).isTrue();
-        assertThat(semanticPropertiesResult.getValue()).hasSize(2);
-
-        // Shared State
-        commitResult = store.editContent()
-                               .add(CONTENT_ID, STREAM_PAYLOAD_SHARED_STATE)
-                               .add(CONTENT_ID_2, STREAM_PAYLOAD_SHARED_STATE_2)
-                               .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<List<StreamSharedState>> sharedStatesResult = store.getSharedStates();
-        assertThat(sharedStatesResult.isSuccessful()).isTrue();
-        assertThat(sharedStatesResult.getValue()).hasSize(2);
-
-        // Journal
-        boolean boolCommitResult =
-                store.editSession(SESSION_ID)
-                        .add(StreamStructure.newBuilder().setContentId(CONTENT_ID).build())
-                        .commit();
-        assertThat(boolCommitResult).isTrue();
-        boolCommitResult =
-                store.editSession(SESSION_ID_2)
-                        .add(StreamStructure.newBuilder().setContentId(CONTENT_ID_2).build())
-                        .commit();
-        assertThat(boolCommitResult).isTrue();
-
-        Result<List<String>> sessionsResult = store.getAllSessions();
-        assertThat(sessionsResult.isSuccessful()).isTrue();
-        assertThat(sessionsResult.getValue()).hasSize(2);
-
-        // Actions
-        commitResult = store.editLocalActions()
-                               .add(ActionType.DISMISS, CONTENT_ID)
-                               .add(ActionType.DISMISS, CONTENT_ID_2)
-                               .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<List<StreamLocalAction>> dismissActionsResult = store.getAllDismissLocalActions();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        assertThat(dismissActionsResult.getValue()).hasSize(2);
-
-        commitResult = store.editUploadableActions()
-                               .upsert(StreamUploadableAction.newBuilder()
-                                               .setFeatureContentId(CONTENT_ID)
-                                               .build(),
-                                       CONTENT_ID)
-                               .upsert(StreamUploadableAction.newBuilder()
-                                               .setFeatureContentId(CONTENT_ID_2)
-                                               .build(),
-                                       CONTENT_ID_2)
-                               .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<Set<StreamUploadableAction>> uploadableActionsResult =
-                store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.isSuccessful()).isTrue();
-        assertThat(uploadableActionsResult.getValue()).hasSize(2);
-
-        /*
-        CLEAR
-        */
-
-        assertThat(store.clearNonActionContent()).isTrue();
-
-        /*
-        VERIFICATION
-        */
-
-        // Payload
-        payloadsResult = store.getPayloads(Collections.singletonList(CONTENT_ID));
-        assertThat(payloadsResult.isSuccessful()).isTrue();
-        assertThat(payloadsResult.getValue()).hasSize(0);
-
-        // Semantic properties (should not be cleared)
-        semanticPropertiesResult =
-                store.getSemanticProperties(Arrays.asList(CONTENT_ID, CONTENT_ID_2));
-        assertThat(semanticPropertiesResult.isSuccessful()).isTrue();
-        assertThat(semanticPropertiesResult.getValue()).hasSize(2);
-        assertThat(semanticPropertiesResult.getValue())
-                .containsExactly(new SemanticPropertiesWithId(CONTENT_ID, SEMANTIC_PROPERTIES),
-                        new SemanticPropertiesWithId(CONTENT_ID_2, SEMANTIC_PROPERTIES_2));
-
-        // Shared state
-        sharedStatesResult = store.getSharedStates();
-        assertThat(sharedStatesResult.isSuccessful()).isTrue();
-        assertThat(sharedStatesResult.getValue()).hasSize(0);
-
-        // Journal
-        sessionsResult = store.getAllSessions();
-        assertThat(sessionsResult.isSuccessful()).isTrue();
-        assertThat(sessionsResult.getValue()).hasSize(0);
-
-        // Actions (should not be cleared)
-        dismissActionsResult = store.getAllDismissLocalActions();
-        assertThat(dismissActionsResult.isSuccessful()).isTrue();
-        assertThat(dismissActionsResult.getValue()).hasSize(2);
-        assertThat(dismissActionsResult.getValue().get(0).getFeatureContentId())
-                .isEqualTo(CONTENT_ID);
-        assertThat(dismissActionsResult.getValue().get(0).getAction())
-                .isEqualTo(ActionType.DISMISS);
-        assertThat(dismissActionsResult.getValue().get(1).getFeatureContentId())
-                .isEqualTo(CONTENT_ID_2);
-        assertThat(dismissActionsResult.getValue().get(1).getAction())
-                .isEqualTo(ActionType.DISMISS);
-
-        // UploadableActions (should be cleared)
-        uploadableActionsResult = store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.isSuccessful()).isTrue();
-        assertThat(uploadableActionsResult.getValue()).isEmpty();
-    }
-
-    @Test
-    public void uploadActions_removedBeforeUpserted_stillUpserts() {
-        PersistentFeedStore store = (PersistentFeedStore) getStore(mMainThreadRunner);
-
-        CommitResult commitResult = store.editUploadableActions()
-                                            .upsert(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID)
-                                                            .build(),
-                                                    CONTENT_ID)
-                                            .upsert(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID_2)
-                                                            .build(),
-                                                    CONTENT_ID_2)
-                                            .remove(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID_2)
-                                                            .build(),
-                                                    CONTENT_ID_2)
-                                            .upsert(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID_2)
-                                                            .build(),
-                                                    CONTENT_ID_2)
-                                            .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<Set<StreamUploadableAction>> uploadableActionsResult =
-                store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.isSuccessful()).isTrue();
-        assertThat(uploadableActionsResult.getValue()).hasSize(2);
-        assertThat(uploadableActionsResult.getValue())
-                .containsAtLeast(
-                        StreamUploadableAction.newBuilder().setFeatureContentId(CONTENT_ID).build(),
-                        StreamUploadableAction.newBuilder()
-                                .setFeatureContentId(CONTENT_ID_2)
-                                .build());
-    }
-
-    @Test
-    public void uploadActions_removedAfterCommittedUpsert_stillRemoves() {
-        PersistentFeedStore store = (PersistentFeedStore) getStore(mMainThreadRunner);
-
-        CommitResult commitResult = store.editUploadableActions()
-                                            .upsert(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID)
-                                                            .build(),
-                                                    CONTENT_ID)
-                                            .commit();
-
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-        Result<Set<StreamUploadableAction>> uploadableActionsResult =
-                store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.getValue()).hasSize(1);
-        assertThat(uploadableActionsResult.getValue())
-                .contains(StreamUploadableAction.newBuilder()
-                                  .setFeatureContentId(CONTENT_ID)
-                                  .build());
-
-        commitResult = store.editUploadableActions()
-                               .remove(StreamUploadableAction.newBuilder()
-                                               .setFeatureContentId(CONTENT_ID)
-                                               .build(),
-                                       CONTENT_ID)
-                               .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        uploadableActionsResult = store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.isSuccessful()).isTrue();
-        assertThat(uploadableActionsResult.getValue()).isEmpty();
-    }
-
-    @Test
-    public void uploadActions_removedNonExistantAction_succeeds() {
-        PersistentFeedStore store = (PersistentFeedStore) getStore(mMainThreadRunner);
-
-        CommitResult commitResult = store.editUploadableActions()
-                                            .remove(StreamUploadableAction.newBuilder()
-                                                            .setFeatureContentId(CONTENT_ID)
-                                                            .build(),
-                                                    CONTENT_ID)
-                                            .commit();
-        assertThat(commitResult).isEqualTo(CommitResult.SUCCESS);
-
-        Result<Set<StreamUploadableAction>> uploadableActionsResult =
-                store.getAllUploadableActions();
-        assertThat(uploadableActionsResult.isSuccessful()).isTrue();
-        assertThat(uploadableActionsResult.getValue()).isEmpty();
-    }
-
-    @Test
-    public void getPayloads_noContent() {
-        Result<List<PayloadWithId>> result =
-                getStore(mMainThreadRunner).getPayloads(ImmutableList.of("foo"));
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).isEmpty();
-    }
-
-    @Test
-    public void getPayloads_withContent() {
-        StreamPayload streamPayload = StreamPayload.newBuilder()
-                                              .setStreamFeature(StreamFeature.getDefaultInstance())
-                                              .build();
-        mContentStorage.commit(
-                new ContentMutation.Builder().upsert("foo", streamPayload.toByteArray()).build());
-
-        Result<List<PayloadWithId>> result =
-                getStore(mMainThreadRunner).getPayloads(ImmutableList.of("foo"));
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).hasSize(1);
-        assertThat(result.getValue().get(0).payload).isEqualTo(streamPayload);
-    }
-
-    @Test
-    public void getPayloads_cannotParse() {
-        mContentStorage.commit(new ContentMutation.Builder().upsert("foo", new byte[] {5}).build());
-
-        Result<List<PayloadWithId>> result =
-                getStore(mMainThreadRunner).getPayloads(ImmutableList.of("foo"));
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).isEmpty();
-        assertThat(mBasicLoggingApi.lastInternalError).isEqualTo(InternalFeedError.ITEM_NOT_PARSED);
-    }
-
-    @Test
-    public void getSharedStates_noContent() {
-        Result<List<StreamSharedState>> result = getStore(mMainThreadRunner).getSharedStates();
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).isEmpty();
-    }
-
-    @Test
-    public void getSharedStates_withContent() {
-        StreamSharedState sharedState = StreamSharedState.newBuilder().setContentId("foo").build();
-        mContentStorage.commit(
-                new ContentMutation.Builder()
-                        .upsert(SHARED_STATE_PREFIX + "bar", sharedState.toByteArray())
-                        .build());
-
-        Result<List<StreamSharedState>> result = getStore(mMainThreadRunner).getSharedStates();
-        assertThat(result.isSuccessful()).isTrue();
-        assertThat(result.getValue()).containsExactly(sharedState);
-    }
-
-    @Test
-    public void getSharedStates_cannotParse() {
-        mContentStorage.commit(new ContentMutation.Builder()
-                                       .upsert(SHARED_STATE_PREFIX + "bar", new byte[] {5})
-                                       .build());
-
-        Result<List<StreamSharedState>> result = getStore(mMainThreadRunner).getSharedStates();
-        assertThat(result.isSuccessful()).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamPayloadInternerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamPayloadInternerTest.java
deleted file mode 100644
index 5761708..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamPayloadInternerTest.java
+++ /dev/null
@@ -1,86 +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.
-
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.intern.WeakPoolInterner;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamLegacyPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link StreamPayloadInterner} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamPayloadInternerTest {
-    private final StreamPayloadInterner mInterner =
-            new StreamPayloadInterner(new WeakPoolInterner<>());
-
-    @Test
-    public void intern() {
-        StreamPayload first =
-                StreamPayload.newBuilder()
-                        .setStreamFeature(
-                                StreamFeature.newBuilder()
-                                        .setContentId(newString("foo"))
-                                        .setParentId(newString("bar"))
-                                        .setLegacyContent(
-                                                StreamLegacyPayload.newBuilder().setType("type")))
-                        .build();
-        StreamPayload second =
-                StreamPayload.newBuilder()
-                        .setStreamSharedState(
-                                StreamSharedState.newBuilder().setContentId(newString("foo")))
-                        .build();
-        StreamPayload third = StreamPayload.newBuilder()
-                                      .setStreamToken(StreamToken.newBuilder()
-                                                              .setContentId(newString("bar"))
-                                                              .setParentId(newString("foo")))
-                                      .build();
-
-        // Sanity check for the newString correct working.
-        assertThat(first.getStreamFeature().getContentId())
-                .isNotSameInstanceAs(second.getStreamSharedState().getContentId());
-        assertThat(first.getStreamFeature().getContentId())
-                .isEqualTo(second.getStreamSharedState().getContentId());
-
-        // Pool is empty so first is added/returned.
-        StreamPayload internedFirst = mInterner.intern(first);
-        assertThat(mInterner.size()).isEqualTo(2); // {foo, bar}.
-        assertThat(internedFirst).isSameInstanceAs(first);
-
-        // Pool already has the "foo" content ID, which is reused.
-        StreamPayload internedSecond = mInterner.intern(second);
-        assertThat(internedSecond).isNotSameInstanceAs(second);
-        assertThat(internedSecond).isEqualTo(second);
-        // Content ID is the same as the one from first.
-        assertThat(mInterner.size()).isEqualTo(2); // {foo, bar}.
-        assertThat(internedSecond.getStreamSharedState().getContentId())
-                .isSameInstanceAs(internedFirst.getStreamFeature().getContentId());
-
-        // Pool already has both "foo" and "bar" content IDs, which are reused.
-        StreamPayload internedThird = mInterner.intern(third);
-        assertThat(internedThird).isNotSameInstanceAs(third);
-        assertThat(internedThird).isEqualTo(third);
-        // Content IDs are both reused.
-        assertThat(mInterner.size()).isEqualTo(2); // {foo, bar}.
-        assertThat(internedThird.getStreamToken().getContentId())
-                .isSameInstanceAs(internedFirst.getStreamFeature().getParentId());
-        assertThat(internedThird.getStreamToken().getParentId())
-                .isSameInstanceAs(internedFirst.getStreamFeature().getContentId());
-    }
-
-    // "new String()" below is called on purpose to generate different String objects.
-    private String newString(String input) {
-        return new String(input);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamStructureInternerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamStructureInternerTest.java
deleted file mode 100644
index 34b4adca..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/internal/StreamStructureInternerTest.java
+++ /dev/null
@@ -1,76 +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.
-
-package org.chromium.chrome.browser.feed.library.feedstore.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.intern.WeakPoolInterner;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamStructure.Operation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link StreamStructureInterner} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamStructureInternerTest {
-    private final StreamStructureInterner mInterner =
-            new StreamStructureInterner(new WeakPoolInterner<>());
-
-    @Test
-    public void intern() {
-        StreamStructure first = StreamStructure.newBuilder()
-                                        .setContentId(newString("foo"))
-                                        .setParentContentId(newString("bar"))
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-        StreamStructure second = StreamStructure.newBuilder()
-                                         .setContentId(newString("foo"))
-                                         .setParentContentId(newString("baz"))
-                                         .setOperation(Operation.UPDATE_OR_APPEND)
-                                         .build();
-        StreamStructure third = StreamStructure.newBuilder()
-                                        .setContentId(newString("bar"))
-                                        .setParentContentId(newString("foo"))
-                                        .setOperation(Operation.UPDATE_OR_APPEND)
-                                        .build();
-
-        // Sanity check for the newString correct working.
-        assertThat(first.getContentId()).isNotSameInstanceAs(second.getContentId());
-        assertThat(first.getContentId()).isEqualTo(second.getContentId());
-
-        // Pool is empty so first is added/returned.
-        StreamStructure internedFirst = mInterner.intern(first);
-        assertThat(mInterner.size()).isEqualTo(2); // {foo, bar}.
-        assertThat(internedFirst).isSameInstanceAs(first);
-
-        // Pool already has the "foo" content ID, which is reused.
-        StreamStructure internedSecond = mInterner.intern(second);
-        assertThat(internedSecond).isNotSameInstanceAs(second);
-        assertThat(internedSecond).isEqualTo(second);
-        // Content ID is the same as the one from first.
-        assertThat(mInterner.size()).isEqualTo(3); // {foo, bar, baz}.
-        assertThat(internedSecond.getContentId()).isSameInstanceAs(internedFirst.getContentId());
-
-        // Pool already has both "foo" and "bar" content IDs, which are reused.
-        StreamStructure internedThird = mInterner.intern(third);
-        assertThat(internedThird).isNotSameInstanceAs(third);
-        assertThat(internedThird).isEqualTo(third);
-        // Content IDs are both reused.
-        assertThat(mInterner.size()).isEqualTo(3); // {foo, bar, baz}.
-        assertThat(internedThird.getContentId())
-                .isSameInstanceAs(internedFirst.getParentContentId());
-        assertThat(internedThird.getParentContentId())
-                .isSameInstanceAs(internedFirst.getContentId());
-    }
-
-    // "new String()" below is called on purpose to generate different String objects.
-    private String newString(String input) {
-        return new String(input);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/network/NetworkClientWrapperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/network/NetworkClientWrapperTest.java
deleted file mode 100644
index 55303a9..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/network/NetworkClientWrapperTest.java
+++ /dev/null
@@ -1,81 +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.
-package org.chromium.chrome.browser.feed.library.hostimpl.network;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.validateMockitoUsage;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.net.Uri;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest.HttpMethod;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpResponse;
-import org.chromium.chrome.browser.feed.library.api.host.network.NetworkClient;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-
-/** Tests of the {@link NetworkClientWrapper}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class NetworkClientWrapperTest {
-    @Mock
-    private ThreadUtils mThreadUtils;
-    @Mock
-    private NetworkClient mNetworkClient;
-    @Mock
-    private Consumer<HttpResponse> mResponseConsumer;
-
-    private HttpRequest mRequest;
-    private final FakeMainThreadRunner mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-
-    @Before
-    public void setup() {
-        mRequest =
-                new HttpRequest(Uri.EMPTY, HttpMethod.GET, Collections.emptyList(), new byte[] {});
-        initMocks(this);
-    }
-
-    @After
-    public void validate() {
-        validateMockitoUsage();
-    }
-
-    @Test
-    public void testSend_mainThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(true);
-        NetworkClientWrapper wrapper =
-                new NetworkClientWrapper(mNetworkClient, mThreadUtils, mMainThreadRunner);
-        wrapper.send(mRequest, mResponseConsumer);
-        verify(mNetworkClient).send(mRequest, mResponseConsumer);
-        assertThat(mMainThreadRunner.hasTasks()).isFalse();
-    }
-
-    @Test
-    public void testSend_backgroundThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(false);
-        NetworkClientWrapper wrapper =
-                new NetworkClientWrapper(mNetworkClient, mThreadUtils, mMainThreadRunner);
-        wrapper.send(mRequest, mResponseConsumer);
-
-        assertThat(mMainThreadRunner.hasTasks()).isTrue();
-        mMainThreadRunner.runAllTasks();
-
-        verify(mNetworkClient).send(mRequest, mResponseConsumer);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/scheduler/SchedulerApiWrapperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/scheduler/SchedulerApiWrapperTest.java
deleted file mode 100644
index 275b3aa..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/scheduler/SchedulerApiWrapperTest.java
+++ /dev/null
@@ -1,110 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.scheduler;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.validateMockitoUsage;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.SessionState;
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link SchedulerApiWrapper}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SchedulerApiWrapperTest {
-    @Mock
-    private ThreadUtils mThreadUtils;
-    @Mock
-    private SchedulerApi mSchedulerApi;
-    private SessionState mSessionState;
-
-    private final FakeMainThreadRunner mMainThreadRunner =
-            FakeMainThreadRunner.runTasksImmediately();
-
-    @Before
-    public void setup() {
-        mSessionState = new SessionState(false, 0L, false);
-        initMocks(this);
-    }
-
-    @After
-    public void validate() {
-        validateMockitoUsage();
-    }
-
-    @Test
-    public void testShouldSessionRequestData_mainThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(true);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.shouldSessionRequestData(mSessionState);
-        verify(mSchedulerApi).shouldSessionRequestData(mSessionState);
-        assertThat(mMainThreadRunner.hasTasks()).isFalse();
-    }
-
-    @Test
-    public void testShouldSessionRequestData_backgroundThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(false);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.shouldSessionRequestData(mSessionState);
-        verify(mSchedulerApi).shouldSessionRequestData(mSessionState);
-        assertThat(mMainThreadRunner.getCompletedTaskCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void testOnReceiveNewContent_mainThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(true);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.onReceiveNewContent(0);
-        verify(mSchedulerApi).onReceiveNewContent(0);
-        assertThat(mMainThreadRunner.hasTasks()).isFalse();
-    }
-
-    @Test
-    public void testOnReceiveNewContent_backgroundThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(false);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.onReceiveNewContent(0);
-        verify(mSchedulerApi).onReceiveNewContent(0);
-        assertThat(mMainThreadRunner.getCompletedTaskCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void testOnRequestError_mainThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(true);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.onRequestError(0);
-        verify(mSchedulerApi).onRequestError(0);
-        assertThat(mMainThreadRunner.hasTasks()).isFalse();
-    }
-
-    @Test
-    public void testOnRequestError_backgroundThread() {
-        when(mThreadUtils.isMainThread()).thenReturn(false);
-        SchedulerApiWrapper wrapper =
-                new SchedulerApiWrapper(mSchedulerApi, mThreadUtils, mMainThreadRunner);
-        wrapper.onRequestError(0);
-        verify(mSchedulerApi).onRequestError(0);
-        assertThat(mMainThreadRunner.getCompletedTaskCount()).isEqualTo(1);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageDirectTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageDirectTest.java
deleted file mode 100644
index 78cd581..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageDirectTest.java
+++ /dev/null
@@ -1,45 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.content.Context;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.ContentStorageDirectConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PersistentContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PersistentContentStorageDirectTest extends ContentStorageDirectConformanceTest {
-    @Mock
-    private ThreadUtils mThreadUtils;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = ApplicationProvider.getApplicationContext();
-        mStorage = new PersistentContentStorage(
-                mContext, MoreExecutors.newDirectExecutorService(), mThreadUtils);
-    }
-
-    @After
-    public void tearDown() {
-        mContext.getFilesDir().delete();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageTest.java
deleted file mode 100644
index 94d3dd3..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentContentStorageTest.java
+++ /dev/null
@@ -1,45 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.content.Context;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.ContentStorageConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PersistentContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PersistentContentStorageTest extends ContentStorageConformanceTest {
-    @Mock
-    private ThreadUtils mThreadUtils;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = ApplicationProvider.getApplicationContext();
-        mStorage = new PersistentContentStorage(
-                mContext, MoreExecutors.newDirectExecutorService(), mThreadUtils);
-    }
-
-    @After
-    public void tearDown() {
-        mContext.getFilesDir().delete();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageDirectTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageDirectTest.java
deleted file mode 100644
index d6bd259..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageDirectTest.java
+++ /dev/null
@@ -1,60 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.content.Context;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.JournalStorageDirectConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PersistentContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PersistentJournalStorageDirectTest extends JournalStorageDirectConformanceTest {
-    private Context mContext;
-    @Mock
-    private ThreadUtils mThreadUtils;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = ApplicationProvider.getApplicationContext();
-        mJournalStorage = new PersistentJournalStorage(
-                mContext, MoreExecutors.directExecutor(), mThreadUtils, null);
-    }
-
-    @After
-    public void tearDown() {
-        mContext.getFilesDir().delete();
-    }
-
-    @Test
-    public void sanitize_and_desanitize() {
-        String[] reservedChars = {"|", "\\", "?", "*", "<", "\"", ":", ">"};
-        for (String c : reservedChars) {
-            String test = "test" + c;
-            String sanitized = ((PersistentJournalStorage) mJournalStorage).sanitize(test);
-            assertThat(sanitized.contains(c)).isFalse();
-            String unsanitized = ((PersistentJournalStorage) mJournalStorage).desanitize(sanitized);
-            assertThat(unsanitized).isEqualTo(test);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageTest.java
deleted file mode 100644
index 8776096..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/PersistentJournalStorageTest.java
+++ /dev/null
@@ -1,60 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.content.Context;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.internal.common.ThreadUtils;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.JournalStorageConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PersistentContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PersistentJournalStorageTest extends JournalStorageConformanceTest {
-    private Context mContext;
-    @Mock
-    private ThreadUtils mThreadUtils;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = ApplicationProvider.getApplicationContext();
-        mJournalStorage = new PersistentJournalStorage(
-                mContext, MoreExecutors.directExecutor(), mThreadUtils, null);
-    }
-
-    @After
-    public void tearDown() {
-        mContext.getFilesDir().delete();
-    }
-
-    @Test
-    public void sanitize_and_desanitize() {
-        String[] reservedChars = {"|", "\\", "?", "*", "<", "\"", ":", ">"};
-        for (String c : reservedChars) {
-            String test = "test" + c;
-            String sanitized = ((PersistentJournalStorage) mJournalStorage).sanitize(test);
-            assertThat(sanitized.contains(c)).isFalse();
-            String unsanitized = ((PersistentJournalStorage) mJournalStorage).desanitize(sanitized);
-            assertThat(unsanitized).isEqualTo(test);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageDirectTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageDirectTest.java
deleted file mode 100644
index 4c1f879..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageDirectTest.java
+++ /dev/null
@@ -1,22 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage.testing;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.ContentStorageDirectConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link InMemoryContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InMemoryContentStorageDirectTest extends ContentStorageDirectConformanceTest {
-    @Before
-    public void setUp() {
-        mStorage = new InMemoryContentStorage();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageTest.java
deleted file mode 100644
index e479a15..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryContentStorageTest.java
+++ /dev/null
@@ -1,23 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage.testing;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingContentStorage;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.ContentStorageConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link InMemoryContentStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InMemoryContentStorageTest extends ContentStorageConformanceTest {
-    @Before
-    public void setUp() {
-        mStorage = new DelegatingContentStorage(new InMemoryContentStorage());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageDirectTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageDirectTest.java
deleted file mode 100644
index 7eb2372..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageDirectTest.java
+++ /dev/null
@@ -1,22 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage.testing;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.JournalStorageDirectConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link InMemoryJournalStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InMemoryJournalStorageDirectTest extends JournalStorageDirectConformanceTest {
-    @Before
-    public void setUp() {
-        mJournalStorage = new InMemoryJournalStorage();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageTest.java
deleted file mode 100644
index 4e60db2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/hostimpl/storage/testing/InMemoryJournalStorageTest.java
+++ /dev/null
@@ -1,26 +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.
-
-package org.chromium.chrome.browser.feed.library.hostimpl.storage.testing;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.feedstore.testing.DelegatingJournalStorage;
-import org.chromium.chrome.browser.feed.library.testing.conformance.storage.JournalStorageConformanceTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link InMemoryJournalStorage}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class InMemoryJournalStorageTest extends JournalStorageConformanceTest {
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mJournalStorage = new DelegatingJournalStorage(new InMemoryJournalStorage());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClearAllTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClearAllTest.java
deleted file mode 100644
index 313a8cb..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClearAllTest.java
+++ /dev/null
@@ -1,108 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.lifecycle.AppLifecycleListener;
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** This will test the behavior of clear all. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ClearAllTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private AppLifecycleListener mAppLifecycleListener;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        Configuration configuration =
-                new Configuration.Builder().put(ConfigKey.LIMIT_PAGE_UPDATES, false).build();
-        InfraIntegrationScope scope =
-                new InfraIntegrationScope.Builder().setConfiguration(configuration).build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mRequestManager = scope.getRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mAppLifecycleListener = scope.getAppLifecycleListener();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-    }
-
-    /**
-     * This test creates two sessions/ModelProviders then will trigger the clear all. We then verify
-     * the expected behavior that the previously created ModelProviders are invalid.
-     */
-    @Test
-    public void testClearAll() {
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3)};
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider1 =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContents(modelProvider1, cards);
-        ModelProvider modelProvider2 =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContents(modelProvider2, cards);
-
-        mAppLifecycleListener.onClearAll();
-
-        assertThat(modelProvider1.getCurrentState()).isEqualTo(State.INVALIDATED);
-        assertThat(modelProvider2.getCurrentState()).isEqualTo(State.INVALIDATED);
-    }
-
-    /**
-     * This test create a session/ModelProvider then calls clear all. It this validates validates
-     * creating a new empty ModelProvider and attempts to create the previously created
-     * ModelProvider.
-     */
-    @Test
-    public void testPostClearAll() {
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3)};
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContents(modelProvider, cards);
-        String modelToken = modelProvider.getSessionId();
-        mAppLifecycleListener.onClearAll();
-
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-
-        // create a new (Empty) ModelProvider
-        modelProvider = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getRootFeature()).isNull();
-
-        // try to create the old existing ModelProvider
-        modelProvider = mModelProviderFactory.create(modelToken, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClientRequestManagerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClientRequestManagerTest.java
deleted file mode 100644
index 8fc696e9..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ClientRequestManagerTest.java
+++ /dev/null
@@ -1,59 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Integration test for {@link
- * org.chromium.chrome.browser.feed.library.feedrequestmanager.RequestManagerImpl}
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ClientRequestManagerTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private RequestManager mRequestManager;
-    private ModelProviderValidator mModelValidator;
-
-    // Create a simple stream with a root and three features
-    private static final ContentId[] CARDS = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3)};
-
-    @Before
-    public void setup() {
-        mRequestManager = mScope.getRequestManager();
-        mModelValidator = new ModelProviderValidator(mScope.getProtocolAdapter());
-    }
-
-    @Test
-    public void testRequestManager() {
-        // Set up new response with 3 cards
-        mScope.getFakeFeedRequestManager().queueResponse(
-                ResponseBuilder.forClearAllWithCards(CARDS).build());
-
-        // Trigger refresh
-        mRequestManager.triggerScheduledRefresh();
-
-        // Create new session
-        ModelProvider modelProvider =
-                mScope.getModelProviderFactory().createNew(null, UiContext.getDefaultInstance());
-
-        // Model provider should now hold the cards
-        mModelValidator.assertCursorContents(modelProvider, CARDS);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentRemoveTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentRemoveTest.java
deleted file mode 100644
index 8c3f1bf..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentRemoveTest.java
+++ /dev/null
@@ -1,112 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.ROOT_CONTENT_ID;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChange;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChangeObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests which remove content within an existing model. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentRemoveTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final FakeFeedRequestManager mFakeFeedRequestManager =
-            mScope.getFakeFeedRequestManager();
-    private final FakeThreadUtils mFakeThreadUtils = mScope.getFakeThreadUtils();
-    private final FeedSessionManager mFeedSessionManager = mScope.getFeedSessionManager();
-    private final ModelProviderFactory mModelProviderFactory = mScope.getModelProviderFactory();
-    private final ModelProviderValidator mModelValidator =
-            new ModelProviderValidator(mScope.getProtocolAdapter());
-    private final RequestManager mRequestManager = mScope.getRequestManager();
-
-    @Test
-    public void removeContent() {
-        // Create a simple stream with a root and four features
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mRequestManager.triggerScheduledRefresh();
-
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getRootFeature()).isNotNull();
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        FeatureChangeObserver observer = mock(FeatureChangeObserver.class);
-        rootFeature.registerObserver(observer);
-        mModelValidator.assertCursorSize(rootFeature.getCursor(), cards.length);
-
-        // Create cursor advanced to each spot in the list of children
-        ModelCursor advancedCursor0 = rootFeature.getCursor();
-        ModelCursor advancedCursor1 = advanceCursor(rootFeature.getCursor(), 1);
-        ModelCursor advancedCursor2 = advanceCursor(rootFeature.getCursor(), 2);
-        ModelCursor advancedCursor3 = advanceCursor(rootFeature.getCursor(), 3);
-        ModelCursor advancedCursor4 = advanceCursor(rootFeature.getCursor(), 4);
-
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.builder().removeFeature(cards[1], ROOT_CONTENT_ID).build());
-        // TODO: sessions reject removes without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
-        ArgumentCaptor<FeatureChange> capture = ArgumentCaptor.forClass(FeatureChange.class);
-        verify(observer).onChange(capture.capture());
-        List<FeatureChange> featureChanges = capture.getAllValues();
-        assertThat(featureChanges).hasSize(1);
-        FeatureChange change = featureChanges.get(0);
-        assertThat(change.getChildChanges().getRemovedChildren()).hasSize(1);
-
-        mModelValidator.assertCursorContents(advancedCursor0, cards[0], cards[2], cards[3]);
-        mModelValidator.assertCursorContents(advancedCursor1, cards[2], cards[3]);
-        mModelValidator.assertCursorContents(advancedCursor2, cards[2], cards[3]);
-        mModelValidator.assertCursorContents(advancedCursor3, cards[3]);
-        mModelValidator.assertCursorContents(advancedCursor4);
-
-        // create a cursor after the remove to verify $HEAD was modified
-        ModelCursor cursor = rootFeature.getCursor();
-        mModelValidator.assertCursorContents(cursor, cards[0], cards[2], cards[3]);
-    }
-
-    private ModelCursor advanceCursor(ModelCursor cursor, int count) {
-        for (int i = 0; i < count; i++) {
-            cursor.getNextItem();
-        }
-        return cursor;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentUpdateTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentUpdateTest.java
deleted file mode 100644
index 6d12350..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ContentUpdateTest.java
+++ /dev/null
@@ -1,125 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChange;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChangeObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests which update content within an existing model. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContentUpdateTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final FakeFeedRequestManager mFakeFeedRequestManager =
-            mScope.getFakeFeedRequestManager();
-    private final FakeThreadUtils mFakeThreadUtils = mScope.getFakeThreadUtils();
-    private final ModelProviderFactory mModelProviderFactory = mScope.getModelProviderFactory();
-    private final ModelProviderValidator mModelValidator =
-            new ModelProviderValidator(mScope.getProtocolAdapter());
-    private final ProtocolAdapter mProtocolAdapter = mScope.getProtocolAdapter();
-    private final FeedSessionManager mFeedSessionManager = mScope.getFeedSessionManager();
-    private final RequestManager mRequestManager = mScope.getRequestManager();
-
-    @Test
-    public void updateContent_observers() {
-        // Create a simple stream with a root and two features
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        List<FeatureChangeObserver> observers = new ArrayList<>();
-        List<FeatureChangeObserver> contentObservers = new ArrayList<>();
-        for (ContentId contentId : cards) {
-            ModelChild child = cursor.getNextItem();
-            assertThat(child).isNotNull();
-            mModelValidator.assertStreamContentId(
-                    child.getContentId(), mProtocolAdapter.getStreamContentId(contentId));
-            mModelValidator.assertCardStructure(child);
-            // register observer on the card
-            ModelFeature feature = child.getModelFeature();
-            FeatureChangeObserver observer = mock(FeatureChangeObserver.class);
-            observers.add(observer);
-            feature.registerObserver(observer);
-
-            // register observer on the content of the card
-            FeatureChangeObserver contentObserver = mock(FeatureChangeObserver.class);
-            contentObservers.add(contentObserver);
-            ModelCursor cardCursor = feature.getCursor();
-            ModelChild cardCursorNextItem = cardCursor.getNextItem();
-            assertThat(cardCursorNextItem).isNotNull();
-            cardCursorNextItem.getModelFeature().registerObserver(contentObserver);
-        }
-        assertThat(cursor.isAtEnd()).isTrue();
-
-        // Create an update response for the two content items
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.builder().addCardsToRoot(cards).build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
-        int id = 0;
-        for (FeatureChangeObserver observer : observers) {
-            ArgumentCaptor<FeatureChange> capture = ArgumentCaptor.forClass(FeatureChange.class);
-            verify(observer).onChange(capture.capture());
-            List<FeatureChange> featureChanges = capture.getAllValues();
-            assertThat(featureChanges).hasSize(1);
-            FeatureChange change = featureChanges.get(0);
-            mModelValidator.assertStreamContentId(
-                    change.getContentId(), mProtocolAdapter.getStreamContentId(cards[id]));
-            assertThat(change.isFeatureChanged()).isTrue();
-            assertThat(change.getChildChanges().getAppendedChildren()).isEmpty();
-            id++;
-        }
-        for (FeatureChangeObserver observer : contentObservers) {
-            ArgumentCaptor<FeatureChange> capture = ArgumentCaptor.forClass(FeatureChange.class);
-            verify(observer).onChange(capture.capture());
-            List<FeatureChange> featureChanges = capture.getAllValues();
-            assertThat(featureChanges).hasSize(1);
-            FeatureChange change = featureChanges.get(0);
-            assertThat(change.isFeatureChanged()).isTrue();
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/DetachSessionTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/DetachSessionTest.java
deleted file mode 100644
index 1ae0363e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/DetachSessionTest.java
+++ /dev/null
@@ -1,159 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.PagingState;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** This will test detaching a session then updating it and reattaching to it */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class DetachSessionTest {
-    // Create a simple stream with a root and three features
-    private static final ContentId[] PAGE_1 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3)};
-    private static final ContentId[] PAGE_2 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(4), ResponseBuilder.createFeatureContentId(5),
-            ResponseBuilder.createFeatureContentId(6)};
-
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private RequestManager mRequestManager;
-
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        Configuration configuration =
-                new Configuration.Builder().put(ConfigKey.LIMIT_PAGE_UPDATES, false).build();
-        InfraIntegrationScope scope =
-                new InfraIntegrationScope.Builder().setConfiguration(configuration).build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mRequestManager = scope.getRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-    }
-
-    /**
-     * Steps in this test:
-     *
-     * <ul>
-     *   <li>Create a sessionOne (ModelProvider) with a token
-     *   <li>Create a sessionTwo matching sessionOne
-     *   <li>Detach sessionOne
-     *   <li>Create an existing session based upon sessionOne
-     *   <li>page sessionTwo
-     *   <li>Verify sessionTwo and the existingSession match
-     * </ul>
-     */
-    @Test
-    public void testPagingDetachedSession() {
-        PagingState s1State = new PagingState(PAGE_1, PAGE_2, 1, mContentIdGenerators);
-
-        mFakeFeedRequestManager.queueResponse(s1State.initialResponse);
-        mRequestManager.triggerScheduledRefresh();
-
-        // Create the sessionOne from page1 and a token
-        ModelProvider sessionOne =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        ModelChild tokenChild = mModelValidator.assertCursorContentsWithToken(sessionOne, PAGE_1);
-
-        // Create the sessionTwo matching sessionOne
-        ModelProvider sessionTwo =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContentsWithToken(sessionTwo, PAGE_1);
-
-        // Detach sessionOne
-        String sessionId = sessionOne.getSessionId();
-        sessionOne.detachModelProvider();
-
-        // recreate sessionOne as existingSession
-        ModelProvider existingSession =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContentsWithToken(existingSession, PAGE_1);
-
-        // page the sessionTwo
-        mFakeFeedRequestManager.queueResponse(s1State.pageResponse);
-        sessionTwo.handleToken(tokenChild.getModelToken());
-
-        // verify that existingSession and sessionTwo match
-        mModelValidator.assertCursorContents(
-                sessionTwo, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-        mModelValidator.assertCursorContents(
-                existingSession, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-    }
-
-    /**
-     * This test differs from the previous test by paging sessionTwo while sessionOne is detached
-     * and then creating the existing session. These should still match.
-     *
-     * <ul>
-     *   <li>Create a sessionOne (ModelProvider) with a token
-     *   <li>Create a sessionTwo matching sessionOne
-     *   <li>Detach sessionOne
-     *   <li>page sessionTwo
-     *   <li>Create an existing session based upon sessionOne
-     *   <li>Verify sessionTwo and the existingSession match
-     * </ul>
-     */
-    @Test
-    public void testPagingDetachedSession_pageWhileDetached() {
-        PagingState s1State = new PagingState(PAGE_1, PAGE_2, 1, mContentIdGenerators);
-
-        mFakeFeedRequestManager.queueResponse(s1State.initialResponse);
-        mRequestManager.triggerScheduledRefresh();
-
-        // Create the sessionOne from page1 and a token
-        ModelProvider sessionOne =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        ModelChild tokenChild = mModelValidator.assertCursorContentsWithToken(sessionOne, PAGE_1);
-
-        // Create the sessionTwo matching sessionOne
-        ModelProvider sessionTwo =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContentsWithToken(sessionTwo, PAGE_1);
-
-        // Detach sessionOne
-        String sessionId = sessionOne.getSessionId();
-        sessionOne.detachModelProvider();
-
-        // page the sessionTwo
-        mFakeFeedRequestManager.queueResponse(s1State.pageResponse);
-        sessionTwo.handleToken(tokenChild.getModelToken());
-
-        // recreate sessionOne as existingSession
-        ModelProvider existingSession =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-
-        // verify that existingSession and sessionTwo match
-        mModelValidator.assertCursorContents(
-                sessionTwo, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-        mModelValidator.assertCursorContents(
-                existingSession, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/EmptyStreamTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/EmptyStreamTest.java
deleted file mode 100644
index 71ee684..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/EmptyStreamTest.java
+++ /dev/null
@@ -1,127 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.WireProtocolInfo;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.libraries.testing.UiContextForTestProto.UiContextForTest;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test which verifies the ModelProvider state for the empty stream cases. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class EmptyStreamTest {
-    private FakeClock mFakeClock;
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeClock = scope.getFakeClock();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void emptyStream_observable() {
-        // ModelProvider will be initialized from empty $HEAD
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getRootFeature()).isNull();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        verify(observer).onSessionStart(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void emptyStream_usesGivenUiContext() {
-        // ModelProvider will be initialized with the given UiContext.
-        UiContext uiContext = UiContext.newBuilder()
-                                      .setExtension(UiContextForTest.uiContextForTest,
-                                              UiContextForTest.newBuilder().setValue(2).build())
-                                      .build();
-        ModelProvider modelProvider = mModelProviderFactory.createNew(null, uiContext);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getRootFeature()).isNull();
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        verify(observer).onSessionStart(uiContext);
-    }
-
-    @Test
-    public void emptyStream_observableInvalidate() {
-        // Verify both ModelProviderObserver events are called
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        verify(observer).onSessionStart(UiContext.getDefaultInstance());
-        modelProvider.invalidate();
-        verify(observer).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void emptyStream_unregisterObservable() {
-        // Verify unregister works, so the ModelProviderObserver is not called for invalidate
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        ModelProviderObserver observer = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(observer);
-        verify(observer).onSessionStart(UiContext.getDefaultInstance());
-        modelProvider.unregisterObserver(observer);
-        modelProvider.invalidate();
-        verify(observer, never()).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void emptyStream_emptyResponse() {
-        // Create an empty stream through a response
-        ResponseBuilder responseBuilder = new ResponseBuilder();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-
-        mFakeClock.advance(TaskQueue.STARVATION_TIMEOUT_MS);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getRootFeature()).isNull();
-
-        WireProtocolInfo protocolInfo = responseBuilder.getWireProtocolInfo();
-        // No features added
-        assertThat(protocolInfo.featuresAdded).hasSize(0);
-        assertThat(protocolInfo.hasClearOperation).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ExistingSessionTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ExistingSessionTest.java
deleted file mode 100644
index 45346221b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ExistingSessionTest.java
+++ /dev/null
@@ -1,195 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests which verify creating a new Model Provider from an existing session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ExistingSessionTest {
-    private static final ContentId[] CARDS = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3)};
-
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final ModelProviderFactory mModelProviderFactory = mScope.getModelProviderFactory();
-    private final ModelProviderValidator mModelValidator =
-            new ModelProviderValidator(mScope.getProtocolAdapter());
-
-    @Before
-    public void setUp() {
-        // Create a simple stream with a root and three features
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(ResponseBuilder.forClearAllWithCards(CARDS).build())
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-    }
-
-    @After
-    public void tearDown() {
-        assertThat(mScope.getTaskQueue().hasBacklog()).isFalse();
-        assertThat(mScope.getFakeMainThreadRunner().hasPendingTasks()).isFalse();
-    }
-
-    @Test
-    public void createModelProvider() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelFeature initRoot = modelProvider.getRootFeature();
-        assertThat(initRoot).isNotNull();
-        ModelCursor initCursor = initRoot.getCursor();
-        assertThat(initCursor).isNotNull();
-
-        String sessionId = modelProvider.getSessionId();
-        assertThat(sessionId).isNotEmpty();
-
-        ModelProvider modelProvider2 =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        assertThat(modelProvider2).isNotNull();
-        ModelFeature root2 = modelProvider2.getRootFeature();
-        assertThat(root2).isNotNull();
-        ModelCursor cursor2 = root2.getCursor();
-        assertThat(cursor2).isNotNull();
-        assertThat(modelProvider2.getSessionId()).isEqualTo(sessionId);
-        mModelValidator.assertCursorContents(cursor2, CARDS);
-
-        // Creating the new session will invalidate the previous ModelProvider and Cursor
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-        assertThat(initCursor.isAtEnd()).isTrue();
-    }
-
-    @Test
-    public void createModelProvider_restoreSession() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        String sessionId = modelProvider.getSessionId();
-        modelProvider.detachModelProvider();
-
-        // Restore the session.
-        ModelProvider restoredModelProvider =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        assertThat(restoredModelProvider).isNotNull();
-        assertThat(restoredModelProvider.getAllRootChildren()).hasSize(CARDS.length);
-    }
-
-    @Test
-    public void createModelProvider_restoreSessionWithOutstandingRequest() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        String sessionId = modelProvider.getSessionId();
-        modelProvider.detachModelProvider();
-
-        // Start a request.
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(
-                        ResponseBuilder.forClearAllWithCards(CARDS).build(), /* delayMs= */ 100)
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-
-        // Restore the session.
-        ModelProvider restoredModelProvider =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        assertThat(restoredModelProvider.getAllRootChildren()).hasSize(CARDS.length);
-
-        // Advance the clock to process the outstanding request.
-        mScope.getFakeClock().advance(100);
-    }
-
-    @Test
-    public void createModelProvider_unknownSession() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelFeature initRoot = modelProvider.getRootFeature();
-        assertThat(initRoot).isNotNull();
-        ModelCursor initCursor = initRoot.getCursor();
-        assertThat(initCursor).isNotNull();
-
-        String sessionId = modelProvider.getSessionId();
-        assertThat(sessionId).isNotEmpty();
-
-        // Create a second model provider using an unknown session token, this should return null
-        ModelProvider modelProvider2 =
-                mModelProviderFactory.create("unknown-session", UiContext.getDefaultInstance());
-        assertThat(modelProvider2).isNotNull();
-        assertThat(modelProvider2.getCurrentState()).isEqualTo(State.INVALIDATED);
-
-        // Now create one from head
-        modelProvider2 = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider2).isNotNull();
-        ModelFeature root2 = modelProvider2.getRootFeature();
-        assertThat(root2).isNotNull();
-        ModelCursor cursor2 = root2.getCursor();
-        assertThat(cursor2).isNotNull();
-        mModelValidator.assertCursorContents(cursor2, CARDS);
-
-        // two sessions against the same $HEAD instance
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider2.getCurrentState()).isEqualTo(State.READY);
-    }
-
-    @Test
-    public void createModelProvider_invalidatedSession() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        String sessionId = modelProvider.getSessionId();
-        assertThat(sessionId).isNotEmpty();
-
-        modelProvider.invalidate();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-
-        // Attempt to connect with the session token we just invalidated.  This will result in a
-        // INVALIDATED ModelProvider.
-        ModelProvider modelProvider2 =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        assertThat(modelProvider2).isNotNull();
-        assertThat(modelProvider2.getCurrentState()).isEqualTo(State.INVALIDATED);
-    }
-
-    @Test
-    public void createModelProvider_detachSession() {
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        String sessionId = modelProvider.getSessionId();
-        assertThat(sessionId).isNotEmpty();
-
-        modelProvider.detachModelProvider();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
-
-        // Attempt to connect with the session token we just detached.  This will work as expected.
-        ModelProvider modelProvider2 =
-                mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        assertThat(modelProvider2).isNotNull();
-        assertThat(modelProvider2.getCurrentState()).isEqualTo(State.READY);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/FilterHeadTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/FilterHeadTest.java
deleted file mode 100644
index c640165..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/FilterHeadTest.java
+++ /dev/null
@@ -1,83 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.Function;
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamPayload;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link FeedSessionManager#getStreamFeaturesFromHead(Function, Consumer)} method. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FilterHeadTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-
-    private FeedSessionManager mFeedSessionManager;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void testFiltering() {
-        ContentId[] cards = new ContentId[] {
-                ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-                ResponseBuilder.createFeatureContentId(5),
-        };
-
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mRequestManager.triggerScheduledRefresh();
-
-        mFeedSessionManager.getStreamFeaturesFromHead(mTransformer, result -> {
-            assertThat(result.isSuccessful()).isTrue();
-            assertThat(result.getValue()).hasSize(11);
-        });
-
-        // only return the contentId of the Content cards
-        mFeedSessionManager.getStreamFeaturesFromHead(mToContent, result -> {
-            assertThat(result.isSuccessful()).isTrue();
-            assertThat(result.getValue()).hasSize(5);
-        });
-    }
-
-    // Return only StreamFeatures
-    private Function<StreamPayload, /*@Nullable*/ StreamFeature> mTransformer = streamPayload
-            -> streamPayload.hasStreamFeature() ? streamPayload.getStreamFeature() : null;
-
-    // Return the contentId from a feature with a RenderableUnit type of CONTENT
-    private Function<StreamPayload, /*@Nullable*/ String> mToContent = streamPayload -> {
-        if (!streamPayload.hasStreamFeature()) {
-            return null;
-        }
-        StreamFeature streamFeature = streamPayload.getStreamFeature();
-        return streamFeature.hasContent() ? streamFeature.getContentId() : null;
-    };
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/GcTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/GcTest.java
deleted file mode 100644
index 1a0cdcd..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/GcTest.java
+++ /dev/null
@@ -1,207 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.common.PayloadWithId;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.host.scheduler.FakeSchedulerApi;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/** Tests that assert the behavior of garbage collection. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class GcTest {
-    private static final ContentId PIET_SHARED_STATE_1 =
-            ContentId.newBuilder()
-                    .setContentDomain("piet-shared-state")
-                    .setId(1)
-                    .setTable("feature")
-                    .build();
-    private static final ContentId PIET_SHARED_STATE_2 =
-            ContentId.newBuilder()
-                    .setContentDomain("piet-shared-state")
-                    .setId(2)
-                    .setTable("feature")
-                    .build();
-    private static final ContentId[] REQUEST_1 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2)};
-    private static final ContentId[] REQUEST_2 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(3), ResponseBuilder.createFeatureContentId(4)};
-    private static final long LIFETIME_MS = Duration.ofHours(1).toMillis();
-    private static final long TIMEOUT_MS = Duration.ofSeconds(5).toMillis();
-
-    private final Configuration mConfiguration =
-            new Configuration.Builder().put(ConfigKey.SESSION_LIFETIME_MS, LIFETIME_MS).build();
-    private final InfraIntegrationScope mScope =
-            new InfraIntegrationScope.Builder()
-                    .setConfiguration(mConfiguration)
-                    .withTimeoutSessionConfiguration(TIMEOUT_MS)
-                    .build();
-
-    @Test
-    public void testGc_contentInLiveSessionRetained() {
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(createResponse(REQUEST_1, PIET_SHARED_STATE_1))
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-
-        // Create a new session based on this request.
-        mScope.getModelProviderFactory()
-                .createNew(/* viewDepthProvider= */ null, UiContext.getDefaultInstance())
-                .detachModelProvider();
-        assertPayloads(REQUEST_1, mScope, /* shouldExist= */ true);
-
-        // Populate HEAD with new data.
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(createResponse(REQUEST_2, PIET_SHARED_STATE_2))
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-
-        // Advance the clock without expiring the first session.
-        mScope.getFakeClock().advance(LIFETIME_MS / 2);
-        InfraIntegrationScope secondScope = mScope.clone();
-        assertPayloads(REQUEST_1, secondScope, /* shouldExist= */ true);
-        assertSharedStates(
-                new ContentId[] {PIET_SHARED_STATE_1}, secondScope, /* shouldExist= */ true);
-        assertPayloads(REQUEST_2, secondScope, /* shouldExist= */ true);
-        assertSharedStates(
-                new ContentId[] {PIET_SHARED_STATE_2}, secondScope, /* shouldExist= */ true);
-    }
-
-    @Test
-    public void testGc_contentInExpiredSessionDeleted() {
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(createResponse(REQUEST_1, PIET_SHARED_STATE_1))
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-
-        // Create a new session based on this request.
-        mScope.getModelProviderFactory()
-                .createNew(/* viewDepthProvider= */ null, UiContext.getDefaultInstance())
-                .detachModelProvider();
-        assertPayloads(REQUEST_1, mScope, /* shouldExist= */ true);
-
-        // Populate HEAD with new data.
-        mScope.getFakeFeedRequestManager()
-                .queueResponse(createResponse(REQUEST_2, PIET_SHARED_STATE_2))
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        mScope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-
-        // Advance the clock to expire the first session, create a new scope that will run
-        // initialization and delete content from the expired session.
-        mScope.getFakeClock().advance(LIFETIME_MS + 1L);
-        InfraIntegrationScope secondScope = mScope.clone();
-        assertPayloads(REQUEST_1, secondScope, /* shouldExist= */ false);
-        assertSharedStates(
-                new ContentId[] {PIET_SHARED_STATE_1}, secondScope, /* shouldExist= */ false);
-        assertPayloads(REQUEST_2, secondScope, /* shouldExist= */ true);
-        assertSharedStates(
-                new ContentId[] {PIET_SHARED_STATE_2}, secondScope, /* shouldExist= */ true);
-    }
-
-    @Test
-    public void testGc_contentBranchedMidInitializationRetained() {
-        InfraIntegrationScope scope =
-                new InfraIntegrationScope.Builder()
-                        .setConfiguration(mConfiguration)
-                        .setSchedulerApi(
-                                new FakeSchedulerApi(FakeThreadUtils.withoutThreadChecks())
-                                        .setRequestBehavior(RequestBehavior.REQUEST_WITH_CONTENT))
-                        .withQueuingTasks()
-                        .withTimeoutSessionConfiguration(TIMEOUT_MS)
-                        .build();
-
-        // Populate HEAD with REQUEST_1.
-        scope.getFakeFeedRequestManager()
-                .queueResponse(createResponse(REQUEST_1, PIET_SHARED_STATE_1))
-                .triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                        scope.getFeedSessionManager().getUpdateConsumer(
-                                MutationContext.EMPTY_CONTEXT));
-        scope.getFakeDirectExecutor().runAllTasks();
-
-        // Make a new scope and enqueue a request to be sent on the next new session. GC should run
-        // after the new session is created and branched off a HEAD containing REQUEST_1.
-        InfraIntegrationScope secondScope = scope.clone();
-        secondScope.getFakeFeedRequestManager().queueResponse(
-                createResponse(REQUEST_2, PIET_SHARED_STATE_2));
-        secondScope.getModelProviderFactory().createNew(
-                /* viewDepthProvider= */ null, UiContext.getDefaultInstance());
-        secondScope.getFakeDirectExecutor().runAllTasks();
-        assertThat(secondScope.getFakeDirectExecutor().hasTasks()).isFalse();
-        assertPayloads(REQUEST_1, secondScope, /* shouldExist= */ true);
-        assertPayloads(REQUEST_2, secondScope, /* shouldExist= */ true);
-    }
-
-    private static void assertPayloads(
-            ContentId[] contentIds, InfraIntegrationScope scope, boolean shouldExist) {
-        scope.getFakeThreadUtils().enforceMainThread(false);
-        for (ContentId contentId : contentIds) {
-            List<PayloadWithId> payloads =
-                    scope.getStore()
-                            .getPayloads(Arrays.asList(new String[] {
-                                    scope.getProtocolAdapter().getStreamContentId(contentId)}))
-                            .getValue();
-            if (shouldExist) {
-                assertThat(payloads).hasSize(1);
-            } else {
-                assertThat(payloads).isEmpty();
-            }
-        }
-    }
-
-    private static void assertSharedStates(
-            ContentId[] contentIds, InfraIntegrationScope scope, boolean shouldExist) {
-        scope.getFakeThreadUtils().enforceMainThread(false);
-        List<String> sharedStateContentIds = new ArrayList<>();
-        for (StreamSharedState streamSharedState : scope.getStore().getSharedStates().getValue()) {
-            sharedStateContentIds.add(streamSharedState.getContentId());
-        }
-        List<String> expectedContentIds = new ArrayList<>(contentIds.length);
-        for (ContentId contentId : contentIds) {
-            expectedContentIds.add(scope.getProtocolAdapter().getStreamContentId(contentId));
-        }
-
-        if (shouldExist) {
-            assertThat(sharedStateContentIds).containsAtLeastElementsIn(expectedContentIds);
-        } else {
-            assertThat(sharedStateContentIds).containsNoneIn(expectedContentIds);
-        }
-    }
-
-    private static Response createResponse(ContentId[] cards, ContentId pietSharedStateContentId) {
-        return ResponseBuilder.builder()
-                .addClearOperation()
-                .addPietSharedState(pietSharedStateContentId)
-                .addRootFeature()
-                .addCardsToRoot(cards)
-                .build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/LimitedPagingTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/LimitedPagingTest.java
deleted file mode 100644
index d7aa84a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/LimitedPagingTest.java
+++ /dev/null
@@ -1,105 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.PagingState;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test which verifies that multiple session page correctly when limited paging is on. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LimitedPagingTest {
-    // Create a simple stream with a root and three features
-    private static final ContentId[] PAGE_1 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3)};
-    private static final ContentId[] PAGE_2 = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(4), ResponseBuilder.createFeatureContentId(5),
-            ResponseBuilder.createFeatureContentId(6)};
-
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private RequestManager mRequestManager;
-
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void testPagingDetachedSession() {
-        PagingState s1State = new PagingState(PAGE_1, PAGE_2, 1, mContentIdGenerators);
-        mFakeFeedRequestManager.queueResponse(s1State.initialResponse);
-        mRequestManager.triggerScheduledRefresh();
-
-        // Create the sessionOne from page1 and a token
-        ModelProvider sessionOne =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        ModelChild tokenChild = mModelValidator.assertCursorContentsWithToken(sessionOne, PAGE_1);
-
-        // Create the sessionTwo matching sessionOne
-        ModelProvider sessionTwo =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContentsWithToken(sessionTwo, PAGE_1);
-
-        // Now Page session one and verify both session are as expected, only session one will
-        // contain the paged content
-        mFakeFeedRequestManager.queueResponse(s1State.pageResponse);
-        sessionOne.handleToken(tokenChild.getModelToken());
-
-        // Create the sessionOne from page1 and a token
-        mModelValidator.assertCursorContents(
-                sessionOne, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-        mModelValidator.assertCursorContentsWithToken(sessionTwo, PAGE_1);
-
-        // Now create a new session from $HEAD to verify that the page response was added to $HEAD
-        ModelProvider sessionThree =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContents(
-                sessionThree, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-
-        // Page session two, this should not change anything else
-        mFakeFeedRequestManager.queueResponse(s1State.pageResponse);
-        sessionTwo.handleToken(tokenChild.getModelToken());
-        mModelValidator.assertCursorContents(
-                sessionOne, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-        mModelValidator.assertCursorContents(
-                sessionTwo, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-        mModelValidator.assertCursorContents(
-                sessionThree, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-
-        // Verify that paging session two did not update $HEAD
-        ModelProvider sessionFour =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertCursorContents(
-                sessionFour, PAGE_1[0], PAGE_1[1], PAGE_1[2], PAGE_2[0], PAGE_2[1], PAGE_2[2]);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/MultiSessionPagingTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/MultiSessionPagingTest.java
deleted file mode 100644
index f31b35c..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/MultiSessionPagingTest.java
+++ /dev/null
@@ -1,154 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.PagingState;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.WireProtocolInfo;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.nio.charset.Charset;
-
-/**
- * This test will create multiple sessions with different content in each. It will then page each
- * session with different tokens to verify that paging sessions doesn't affect sessions without the
- * paging token.
- *
- * <p>The test runs the following tasks:
- *
- * <ol>
- *   <li>Create the initial response and page response for Session 1 and 2, including tokens
- *   <li>Create Session 1 and $HEAD from Session 1 initial response
- *   <li>Create Session 2 from $HEAD (Session 1 initial response)
- *   <li>Refresh Session 2 (directly using the protocol adapter) using Session 2 initial response.
- *       This creates a new $HEAD and invalidates the Session 2 Model Provider
- *   <li>Create a new Session 2 against the new $HEAD
- *   <li>Validate that Session 1 and 2 have the expected content
- *   <li>Page Session 1 and validate that both sessions have expected content
- *   <li>Page Session 2 and validate that both sessions have expected content
- * </ol>
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class MultiSessionPagingTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private FeedSessionManager mFeedSessionManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void testPaging() {
-        // Create session 1 content
-        ContentId[] s1Cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        ContentId[] s1PageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-        PagingState s1State = new PagingState(s1Cards, s1PageCards, 1, mContentIdGenerators);
-        ByteString token1 = ByteString.copyFrom("s1-page", Charset.defaultCharset());
-        ResponseBuilder s1InitialResponse = getInitialResponse(s1Cards, token1);
-
-        // Create session 2 content
-        ContentId[] s2Cards = new ContentId[] {ResponseBuilder.createFeatureContentId(101),
-                ResponseBuilder.createFeatureContentId(102)};
-        ContentId[] s2PageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(103),
-                ResponseBuilder.createFeatureContentId(104)};
-        PagingState s2State = new PagingState(s2Cards, s2PageCards, 2, mContentIdGenerators);
-
-        // Create an initial S1 $HEAD session
-        mFakeFeedRequestManager.queueResponse(s1State.initialResponse);
-        mRequestManager.triggerScheduledRefresh();
-
-        ModelProvider mp1 = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertRoot(mp1);
-        WireProtocolInfo protocolInfo = s1InitialResponse.getWireProtocolInfo();
-        assertThat(protocolInfo.hasToken).isTrue();
-        ModelChild mp1Token = mModelValidator.assertCursorContentsWithToken(mp1, s1Cards);
-        assertThat(mp1.getCurrentState()).isEqualTo(State.READY);
-
-        // Create a second session against the S1 head.
-        ModelProvider mp2 = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(mp2.getCurrentState()).isEqualTo(State.READY);
-        mModelValidator.assertCursorContentsWithToken(mp2, s1Cards);
-
-        // Refresh the Stream with the S2 initial response
-        mFakeFeedRequestManager.queueResponse(s2State.initialResponse);
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(
-                        new MutationContext.Builder()
-                                .setRequestingSessionId(mp2.getSessionId())
-                                .build()));
-        assertThat(mp1.getCurrentState()).isEqualTo(State.READY);
-        assertThat(mp2.getCurrentState()).isEqualTo(State.INVALIDATED);
-
-        // Now create a ModelProvider against the new session.
-        mp2 = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertRoot(mp2);
-        protocolInfo = s1InitialResponse.getWireProtocolInfo();
-        assertThat(protocolInfo.hasToken).isTrue();
-        ModelChild mp2Token = mModelValidator.assertCursorContentsWithToken(mp2, s2Cards);
-        assertThat(mp2.getCurrentState()).isEqualTo(State.READY);
-
-        // Verify that we didn't change the first session
-        mModelValidator.assertCursorContentsWithToken(mp1, s1Cards);
-        assertThat(mp1.getCurrentState()).isEqualTo(State.READY);
-
-        // now page S1
-        mFakeFeedRequestManager.queueResponse(s1State.pageResponse);
-        mp1.handleToken(mp1Token.getModelToken());
-        mModelValidator.assertCursorContents(
-                mp1, s1Cards[0], s1Cards[1], s1PageCards[0], s1PageCards[1]);
-        mModelValidator.assertCursorContentsWithToken(mp2, s2Cards);
-
-        // now page S2
-        mFakeFeedRequestManager.queueResponse(s2State.pageResponse);
-        mp2.handleToken(mp2Token.getModelToken());
-        mModelValidator.assertCursorContents(
-                mp1, s1Cards[0], s1Cards[1], s1PageCards[0], s1PageCards[1]);
-        mModelValidator.assertCursorContents(
-                mp2, s2Cards[0], s2Cards[1], s2PageCards[0], s2PageCards[1]);
-    }
-
-    private ResponseBuilder getInitialResponse(ContentId[] cards, ByteString token) {
-        return ResponseBuilder.forClearAllWithCards(cards).addStreamToken(1, token);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithContentTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithContentTest.java
deleted file mode 100644
index 8218702..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithContentTest.java
+++ /dev/null
@@ -1,82 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the NO_REQUEST_WITH_CONTENT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class NoRequestWithContentTest {
-    private final SessionTestUtils mUtils =
-            new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_CONTENT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_shouldShowContentImmediatelyAndAppend() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        // REQUEST_2 items should be appended.
-        mScope.getFakeClock().advance(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        // REQUEST_2 items should not be appended.
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_shouldShowContentImmediately() {
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithTimeoutTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithTimeoutTest.java
deleted file mode 100644
index 40cc64a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithTimeoutTest.java
+++ /dev/null
@@ -1,167 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the NO_REQUEST_WITH_TIMEOUT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class NoRequestWithTimeoutTest {
-    private final SessionTestUtils mUtils =
-            new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_TIMEOUT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentWithError() {
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutAndAppend() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutWithError() {
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnTimeout() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnTimeoutWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_shouldShowContentImmediately() {
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithWaitTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithWaitTest.java
deleted file mode 100644
index 663865d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/NoRequestWithWaitTest.java
+++ /dev/null
@@ -1,105 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the NO_REQUEST_WITH_WAIT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class NoRequestWithWaitTest {
-    private final SessionTestUtils mUtils =
-            new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_WAIT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnFailure() {
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnFailure() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_shouldShowContentImmediately() {
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RemoveTrackingBehaviorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RemoveTrackingBehaviorTest.java
deleted file mode 100644
index ac967e1..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RemoveTrackingBehaviorTest.java
+++ /dev/null
@@ -1,183 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.ROOT_CONTENT_ID;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.Function;
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.RemoveTrackingFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.RemoveTracking;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamDataOperation;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/** Tests of the ModelProvider RemoveTracking behavior. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RemoveTrackingBehaviorTest {
-    private static final ContentId[] CARDS = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1),
-            ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3),
-            ResponseBuilder.createFeatureContentId(4),
-            ResponseBuilder.createFeatureContentId(5),
-    };
-
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private FeedSessionManager mFeedSessionManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ProtocolAdapter mProtocolAdapter;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mProtocolAdapter = scope.getProtocolAdapter();
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void testBaseRemoveTracking() {
-        loadInitialData();
-
-        AtomicBoolean called = new AtomicBoolean(false);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        modelProvider.enableRemoveTracking(getRemoveTrackingFactory((contentIds) -> {
-            assertThat(contentIds).hasSize(1);
-            called.set(true);
-        }));
-        ResponseBuilder responseBuilder =
-                ResponseBuilder.builder().removeFeature(CARDS[1], ROOT_CONTENT_ID);
-        List<StreamDataOperation> dataOperations = getDataOperations(responseBuilder);
-        MutationContext mutationContext =
-                new MutationContext.Builder().setUserInitiated(true).build();
-        Consumer<Result<Model>> updateConsumer =
-                mFeedSessionManager.getUpdateConsumer(mutationContext);
-        updateConsumer.accept(Result.success(Model.of(dataOperations)));
-        assertThat(called.get()).isTrue();
-    }
-
-    @Test
-    public void testBaseRemoveTracking_multipleItems() {
-        loadInitialData();
-
-        AtomicBoolean called = new AtomicBoolean(false);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        modelProvider.enableRemoveTracking(getRemoveTrackingFactory((contentIds) -> {
-            assertThat(contentIds).hasSize(2);
-            called.set(true);
-        }));
-        ResponseBuilder responseBuilder = ResponseBuilder.builder()
-                                                  .removeFeature(CARDS[1], ROOT_CONTENT_ID)
-                                                  .removeFeature(CARDS[3], ROOT_CONTENT_ID);
-        List<StreamDataOperation> dataOperations = getDataOperations(responseBuilder);
-        MutationContext mutationContext =
-                new MutationContext.Builder().setUserInitiated(true).build();
-        Consumer<Result<Model>> updateConsumer =
-                mFeedSessionManager.getUpdateConsumer(mutationContext);
-        updateConsumer.accept(Result.success(Model.of(dataOperations)));
-        assertThat(called.get()).isTrue();
-    }
-
-    /**
-     * For non-user initiated mutations, the test is setup to return null from the factory. The
-     * result is the Consumer should not be called.
-     */
-    @Test
-    public void testNonUserInitiated() {
-        loadInitialData();
-
-        AtomicBoolean called = new AtomicBoolean(false);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        modelProvider.enableRemoveTracking(getRemoveTrackingFactory((contentIds) -> {
-            assertThat(contentIds).isEmpty();
-            called.set(true);
-        }));
-
-        ResponseBuilder responseBuilder =
-                ResponseBuilder.builder().removeFeature(CARDS[1], ROOT_CONTENT_ID);
-        List<StreamDataOperation> dataOperations = getDataOperations(responseBuilder);
-        MutationContext mutationContext =
-                new MutationContext.Builder().setUserInitiated(false).build();
-        Consumer<Result<Model>> updateConsumer =
-                mFeedSessionManager.getUpdateConsumer(mutationContext);
-        updateConsumer.accept(Result.success(Model.of(dataOperations)));
-        assertThat(called.get()).isFalse();
-    }
-
-    private RemoveTrackingFactory<String> getRemoveTrackingFactory(
-            Consumer<List<String>> consumer) {
-        return new RemoveTrackingFactory<String>() {
-            @Override
-            public /*@Nullable*/ RemoveTracking<String> create(MutationContext mutationContext) {
-                // Only support RemoveTracking on user initiated removes
-                return mutationContext.isUserInitiated() ? getRemoveTracking(
-                               (streamFeature) -> simpleTransform(streamFeature), consumer)
-                                                         : null;
-            }
-        };
-    }
-
-    private RemoveTracking<String> getRemoveTracking(
-            Function<StreamFeature, String> transformer, Consumer<List<String>> consumer) {
-        return new RemoveTracking<>(transformer, consumer);
-    }
-
-    @SuppressWarnings("unused")
-    private boolean alwaysTrue(String value) {
-        return true;
-    }
-
-    private String simpleTransform(StreamFeature streamFeature) {
-        // only return the Content StreamFeatures.
-        return streamFeature.hasContent() ? streamFeature.getContentId() : null;
-    }
-
-    private List<StreamDataOperation> getDataOperations(ResponseBuilder builder) {
-        Response response = builder.build();
-        Result<Model> result = mProtocolAdapter.createModel(response);
-        assertThat(result.isSuccessful()).isTrue();
-        return result.getValue().streamDataOperations;
-    }
-
-    private void loadInitialData() {
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(CARDS).build());
-        mRequestManager.triggerScheduledRefresh();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithContentTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithContentTest.java
deleted file mode 100644
index 1746abb..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithContentTest.java
+++ /dev/null
@@ -1,93 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the REQUEST_WITH_CONTENT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class RequestWithContentTest {
-    private final SessionTestUtils mUtils =
-            new SessionTestUtils(RequestBehavior.REQUEST_WITH_CONTENT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_shouldShowContentImmediatelyAndAppend() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        // REQUEST_2 items should be appended.
-        mScope.getFakeClock().advance(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        // REQUEST_2 items should not be appended.
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_shouldShowZeroState() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        // REQUEST_2 items should not be appended.
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_shouldShowContentImmediatelyAndAppend() {
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        // REQUEST_2 items should be appended.
-        mScope.getFakeClock().advance(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithTimeoutTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithTimeoutTest.java
deleted file mode 100644
index 9a32aa0..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithTimeoutTest.java
+++ /dev/null
@@ -1,263 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the REQUEST_WITH_TIMEOUT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class RequestWithTimeoutTest {
-    private final SessionTestUtils mUtils =
-            new SessionTestUtils(RequestBehavior.REQUEST_WITH_TIMEOUT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentWithError() {
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutAndAppend() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutWithError() {
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnTimeout() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnTimeoutWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenZeroStateWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.requestBeforeTimeout().queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenZeroStateOnTimeout() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenZeroStateOnTimeoutWithError() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.requestBeforeTimeout().queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContentWithError() {
-        long delayMs = mUtils.requestBeforeTimeout().queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContentOnTimeoutAndAppend() {
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertAppendedContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContentOnTimeoutWithError() {
-        long delayMs = mUtils.queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-
-        mScope.getFakeClock().advanceTo(delayMs);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithWaitTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithWaitTest.java
deleted file mode 100644
index 5a9d58f7..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RequestWithWaitTest.java
+++ /dev/null
@@ -1,137 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.SessionTestUtils;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests the REQUEST_WITH_WAIT behavior for creating a new session. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class RequestWithWaitTest {
-    private final SessionTestUtils mUtils = new SessionTestUtils(RequestBehavior.REQUEST_WITH_WAIT);
-    private final InfraIntegrationScope mScope = mUtils.getScope();
-
-    @Before
-    public void setUp() {
-        mUtils.populateHead();
-    }
-
-    @After
-    public void tearDown() {
-        mUtils.assertWorkComplete();
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentWithRequest_spinnerThenShowContentOnFailure() {
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentWithRequest_spinnerThenZeroStateOnFailure() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.startOutstandingRequestWithError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenShowContent() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_noContentNoRequest_spinnerThenZeroStateOnFailure() {
-        mScope.getAppLifecycleListener().onClearAll();
-        long delayMs = mUtils.queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getAllRootChildren()).isEmpty();
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContent() {
-        long delayMs = mUtils.queueRequest();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertNewContent(modelProvider.getAllRootChildren());
-    }
-
-    @Test
-    public void test_hasContentNoRequest_spinnerThenShowContentOnFailure() {
-        long delayMs = mUtils.queueError();
-
-        ModelProvider modelProvider = mUtils.createNewSession();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
-
-        mScope.getFakeClock().advance(delayMs);
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mUtils.assertHeadContent(modelProvider.getAllRootChildren());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RootOnlyTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RootOnlyTest.java
deleted file mode 100644
index ee984905..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/RootOnlyTest.java
+++ /dev/null
@@ -1,142 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.WireProtocolInfo;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of a Stream with only a root. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RootOnlyTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final FakeFeedRequestManager mFakeFeedRequestManager =
-            mScope.getFakeFeedRequestManager();
-    private final FakeThreadUtils mFakeThreadUtils = mScope.getFakeThreadUtils();
-    private final ModelProviderFactory mModelProviderFactory = mScope.getModelProviderFactory();
-    private final ModelProviderValidator mModelValidator =
-            new ModelProviderValidator(mScope.getProtocolAdapter());
-    private final FeedSessionManager mFeedSessionManager = mScope.getFeedSessionManager();
-    private final RequestManager mRequestManager = mScope.getRequestManager();
-
-    @Test
-    public void rootOnlyResponse_beforeSessionWithLifecycle() {
-        // ModelProvider is created from $HEAD containing content
-        ResponseBuilder responseBuilder =
-                new ResponseBuilder().addClearOperation().addRootFeature();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelProviderObserver changeObserver = mock(ModelProviderObserver.class);
-        modelProvider.registerObserver(changeObserver);
-        verify(changeObserver).onSessionStart(UiContext.getDefaultInstance());
-        verify(changeObserver, never()).onSessionFinished(any(UiContext.class));
-
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mModelValidator.assertRoot(modelProvider);
-
-        WireProtocolInfo protocolInfo = responseBuilder.getWireProtocolInfo();
-        // 1 root
-        assertThat(protocolInfo.featuresAdded).hasSize(1);
-        assertThat(protocolInfo.hasClearOperation).isTrue();
-
-        modelProvider.invalidate();
-        verify(changeObserver).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void rootOnlyResponse_afterSessionWithLifecycle() {
-        // ModelProvider created from empty $HEAD, followed by a response adding head
-        // Verify the observer lifecycle is correctly called
-        ModelProviderObserver changeObserver = mock(ModelProviderObserver.class);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        modelProvider.registerObserver(changeObserver);
-        verify(changeObserver).onSessionStart(UiContext.getDefaultInstance());
-        verify(changeObserver, never()).onSessionFinished(any(UiContext.class));
-
-        ResponseBuilder responseBuilder = new ResponseBuilder().addRootFeature();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        verify(changeObserver, never()).onSessionFinished(any(UiContext.class));
-
-        assertThat(modelProvider).isNotNull();
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        mModelValidator.assertRoot(modelProvider);
-
-        WireProtocolInfo protocolInfo = responseBuilder.getWireProtocolInfo();
-        // 1 root
-        assertThat(protocolInfo.featuresAdded).hasSize(1);
-        assertThat(protocolInfo.hasClearOperation).isFalse();
-
-        modelProvider.invalidate();
-        verify(changeObserver).onSessionFinished(UiContext.getDefaultInstance());
-    }
-
-    @Test
-    public void rootOnlyResponse_setSecondRoot() {
-        // Set the root in two different responses, verify the lifecycle is called correctly
-        // and the root is replaced
-        ModelProviderObserver changeObserver = mock(ModelProviderObserver.class);
-        ResponseBuilder responseBuilder =
-                new ResponseBuilder().addClearOperation().addRootFeature();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        modelProvider.registerObserver(changeObserver);
-        mModelValidator.assertRoot(modelProvider);
-
-        ContentId anotherRoot = ContentId.newBuilder()
-                                        .setContentDomain("root-feature")
-                                        .setId(2)
-                                        .setTable("feature")
-                                        .build();
-        responseBuilder = new ResponseBuilder().addRootFeature(anotherRoot);
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        verify(changeObserver).onSessionFinished(any(UiContext.class));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SemanticPropertiesTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SemanticPropertiesTest.java
deleted file mode 100644
index c8576d2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SemanticPropertiesTest.java
+++ /dev/null
@@ -1,63 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.protobuf.ByteString;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.SemanticPropertiesWithId;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.store.Store;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Tests around Semantic Properties */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SemanticPropertiesTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final FakeFeedRequestManager mFakeFeedRequestManager =
-            mScope.getFakeFeedRequestManager();
-    private final RequestManager mRequestManager = mScope.getRequestManager();
-    private final Store mStore = mScope.getStore();
-
-    @Test
-    public void persistingSemanticProperties() {
-        ContentId contentId = ResponseBuilder.createFeatureContentId(13);
-        ByteString semanticData = ByteString.copyFromUtf8("helloWorld");
-
-        Response response = new ResponseBuilder()
-                                    .addClearOperation()
-                                    .addCardWithSemanticData(contentId, semanticData)
-                                    .build();
-        mFakeFeedRequestManager.queueResponse(response);
-        mRequestManager.triggerScheduledRefresh();
-
-        mScope.getFakeThreadUtils().enforceMainThread(false);
-        ContentIdGenerators idGenerators = new ContentIdGenerators();
-        String contentIdString = idGenerators.createContentId(contentId);
-        Result<List<SemanticPropertiesWithId>> semanticPropertiesResult =
-                mStore.getSemanticProperties(Collections.singletonList(contentIdString));
-        assertThat(semanticPropertiesResult.isSuccessful()).isTrue();
-        List<SemanticPropertiesWithId> semanticProperties = semanticPropertiesResult.getValue();
-        assertThat(semanticProperties).hasSize(1);
-        assertThat(semanticProperties.get(0).contentId).isEqualTo(contentIdString);
-        assertThat(semanticProperties.get(0).semanticData).isEqualTo(semanticData.toByteArray());
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SharedStateTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SharedStateTest.java
deleted file mode 100644
index 79ed4ce..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SharedStateTest.java
+++ /dev/null
@@ -1,85 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamSharedState;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of accessing shared state. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SharedStateTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ProtocolAdapter mProtocolAdapter;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mProtocolAdapter = scope.getProtocolAdapter();
-        mModelValidator = new ModelProviderValidator(mProtocolAdapter);
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void sharedState_headBeforeModelProvider() {
-        // ModelProvider is created from $HEAD containing content, simple shared state added
-        ResponseBuilder responseBuilder =
-                new ResponseBuilder().addClearOperation().addPietSharedState().addRootFeature();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        StreamSharedState sharedState =
-                modelProvider.getSharedState(ResponseBuilder.PIET_SHARED_STATE);
-        assertThat(sharedState).isNotNull();
-        mModelValidator.assertStreamContentId(sharedState.getContentId(),
-                mProtocolAdapter.getStreamContentId(ResponseBuilder.PIET_SHARED_STATE));
-    }
-
-    @Test
-    public void sharedState_headAfterModelProvider() {
-        // ModelProvider is created from empty $HEAD, simple shared state added
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        StreamSharedState sharedState =
-                modelProvider.getSharedState(ResponseBuilder.PIET_SHARED_STATE);
-        assertThat(sharedState).isNull();
-
-        ResponseBuilder responseBuilder =
-                new ResponseBuilder().addClearOperation().addPietSharedState().addRootFeature();
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-
-        sharedState = modelProvider.getSharedState(ResponseBuilder.PIET_SHARED_STATE);
-        assertThat(sharedState).isNotNull();
-        mModelValidator.assertStreamContentId(sharedState.getContentId(),
-                mProtocolAdapter.getStreamContentId(ResponseBuilder.PIET_SHARED_STATE));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SimpleStreamTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SimpleStreamTest.java
deleted file mode 100644
index a8f8ce1..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SimpleStreamTest.java
+++ /dev/null
@@ -1,118 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.WireProtocolInfo;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Simple tests of a stream with multiple cards. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SimpleStreamTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private ProtocolAdapter mProtocolAdapter;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mProtocolAdapter = scope.getProtocolAdapter();
-        mModelValidator = new ModelProviderValidator(mProtocolAdapter);
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void simpleStream_oneCard() {
-        // ModelProvider created after $HEAD has content, one root, and one Card
-        // A Card is two features, the Card and Content.
-        ResponseBuilder responseBuilder = ResponseBuilder.forClearAllWithCards(
-                new ContentId[] {ResponseBuilder.createFeatureContentId(1)});
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertRoot(modelProvider);
-
-        WireProtocolInfo protocolInfo = responseBuilder.getWireProtocolInfo();
-        int expectedFeatureCount = 3; // 1 root, 1 Card (2 features)
-        assertThat(protocolInfo.featuresAdded).hasSize(expectedFeatureCount);
-        assertThat(protocolInfo.hasClearOperation).isTrue();
-        mModelValidator.verifyContent(modelProvider, protocolInfo.featuresAdded);
-
-        // Validate the cursors
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        int cursorCount = 0;
-        while (cursor.getNextItem() != null) {
-            cursorCount++;
-        }
-        assertThat(cursorCount).isEqualTo(1);
-
-        // Validate that the structure of the card
-        cursor = root.getCursor();
-        ModelChild child = cursor.getNextItem();
-        mModelValidator.assertCardStructure(child);
-    }
-
-    @Test
-    public void simpleStream_twoCard() {
-        // ModelProvider created after $HEAD has content, one root, and two Cards
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        ResponseBuilder responseBuilder = ResponseBuilder.forClearAllWithCards(cards);
-        mFakeFeedRequestManager.queueResponse(responseBuilder.build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertRoot(modelProvider);
-
-        WireProtocolInfo protocolInfo = responseBuilder.getWireProtocolInfo();
-        int expectedFeatureCount = 5; // 1 root, 2 Card (* 2 features)
-        assertThat(protocolInfo.featuresAdded).hasSize(expectedFeatureCount);
-        assertThat(protocolInfo.hasClearOperation).isTrue();
-        mModelValidator.verifyContent(modelProvider, protocolInfo.featuresAdded);
-
-        // Validate the cursor, we should have one card for each added
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        for (ContentId contentId : cards) {
-            ModelChild child = cursor.getNextItem();
-            assertThat(child).isNotNull();
-            mModelValidator.assertStreamContentId(
-                    child.getContentId(), mProtocolAdapter.getStreamContentId(contentId));
-            mModelValidator.assertCardStructure(child);
-        }
-        assertThat(cursor.isAtEnd()).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StreamPagingTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StreamPagingTest.java
deleted file mode 100644
index 6707965..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StreamPagingTest.java
+++ /dev/null
@@ -1,164 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelToken;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompleted;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompletedObserver;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.PagingState;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Test of handling paging operations within the Stream. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamPagingTest {
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-        mRequestManager = scope.getRequestManager();
-    }
-
-    /**
-     * This test will create a Session and page it. Fully validating the observer and results.
-     *
-     * <ol>
-     *   <li>Setup the initial response and the paged response
-     *   <li>Setup the paging handling for the FeedRequestManager
-     *   <li>Create the Initial $HEAD from the initial response
-     *   <li>Create a Session/ModelProvider
-     *   <li>Setup the MutationContext and the ModelToken observer
-     *   <li>ModelProvider.handleToken to cause the paging response to be loaded
-     *   <li>Validate...
-     * </ol>
-     */
-    @Test
-    public void testPaging() {
-        // ModelProvider created after $HEAD has content, one root, and two Cards and a token
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        ContentId[] pageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-        PagingState state = new PagingState(cards, pageCards, 1, mContentIdGenerators);
-        mFakeFeedRequestManager.queueResponse(state.initialResponse);
-        mFakeFeedRequestManager.queueResponse(state.pageResponse);
-
-        // Create an initial model
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        mModelValidator.assertRoot(modelProvider);
-
-        // Validate the structure of the stream after the initial response
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor cursor = rootFeature.getCursor();
-        ModelChild tokenFeature = mModelValidator.assertCursorContentsWithToken(cursor, cards);
-        assertThat(tokenFeature).isNotNull();
-
-        // Add an observer to the Token to get an event when the response is processed
-        TokenCompletedObserver tokenCompletedObserver = mock(TokenCompletedObserver.class);
-        ModelToken modelToken = tokenFeature.getModelToken();
-        assertThat(modelToken).isNotNull();
-
-        // Capture the event triggered once the paging operation is finished
-        modelToken.registerObserver(tokenCompletedObserver);
-        modelProvider.handleToken(modelToken);
-        ArgumentCaptor<TokenCompleted> completedArgumentCaptor =
-                ArgumentCaptor.forClass(TokenCompleted.class);
-        verify(tokenCompletedObserver).onTokenCompleted(completedArgumentCaptor.capture());
-        ModelCursor pageCursor = completedArgumentCaptor.getValue().getCursor();
-        assertThat(pageCursor).isNotNull();
-
-        // The event cursor will have only the new items added in the page
-        mModelValidator.assertCursorContents(pageCursor, pageCards);
-
-        // The full cursor should now contain all the features
-        cursor = rootFeature.getCursor();
-        mModelValidator.assertCursorContents(
-                cursor, cards[0], cards[1], pageCards[0], pageCards[1]);
-    }
-
-    /**
-     * This test will create a session and page it. Then create a new session from $HEAD to verify
-     * that Head is correctly handling the token add/remove combination.
-     *
-     * <ul>
-     *   <li>Setup the initial response and the paged response
-     *   <li>Setup the paging handling for the FeedRequestManager
-     *   <li>Create the Initial $HEAD from the initial response
-     *   <li>Create a Session/ModelProvider
-     *   <li>Page the Session/ModelProvider
-     *   <li>Create a new Session from $HEAD
-     *   <li>Validate that the PageToken are not in the cursor
-     * </ul>
-     */
-    @Test
-    public void testPostPagingSessionCreation() {
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        ContentId[] pageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-        PagingState state = new PagingState(cards, pageCards, 1, mContentIdGenerators);
-        mFakeFeedRequestManager.queueResponse(state.initialResponse);
-        mFakeFeedRequestManager.queueResponse(state.pageResponse);
-
-        // Create an initial model
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        ModelFeature rootFeature = modelProvider.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        ModelCursor cursor = rootFeature.getCursor();
-        ModelChild tokenFeature = mModelValidator.assertCursorContentsWithToken(cursor, cards);
-        modelProvider.handleToken(tokenFeature.getModelToken());
-        cursor = rootFeature.getCursor();
-        mModelValidator.assertCursorContents(
-                cursor, cards[0], cards[1], pageCards[0], pageCards[1]);
-
-        // Now create a second ModelProvider and verify the cursor.
-        ModelProvider session2 =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        rootFeature = session2.getRootFeature();
-        assertThat(rootFeature).isNotNull();
-        cursor = rootFeature.getCursor();
-        mModelValidator.assertCursorContents(
-                cursor, cards[0], cards[1], pageCards[0], pageCards[1]);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StructureUpdateTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StructureUpdateTest.java
deleted file mode 100644
index 2da2028d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/StructureUpdateTest.java
+++ /dev/null
@@ -1,137 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChange;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChangeObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests which update (append) content to an existing model. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StructureUpdateTest {
-    private final InfraIntegrationScope mScope = new InfraIntegrationScope.Builder().build();
-    private final FakeFeedRequestManager mFakeFeedRequestManager =
-            mScope.getFakeFeedRequestManager();
-    private final FakeThreadUtils mFakeThreadUtils = mScope.getFakeThreadUtils();
-    private final ModelProviderFactory mModelProviderFactory = mScope.getModelProviderFactory();
-    private final ModelProviderValidator mModelValidator =
-            new ModelProviderValidator(mScope.getProtocolAdapter());
-    private final ProtocolAdapter mProtocolAdapter = mScope.getProtocolAdapter();
-    private final FeedSessionManager mFeedSessionManager = mScope.getFeedSessionManager();
-    private final RequestManager mRequestManager = mScope.getRequestManager();
-
-    @Test
-    public void appendChildren() {
-        // Create a simple stream with a root and two features
-        ContentId[] startingCards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        // Define two features to be appended to the root
-        ContentId[] appendedCards = new ContentId[] {ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(startingCards).build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        FeatureChangeObserver rootObserver = mock(FeatureChangeObserver.class);
-        root.registerObserver(rootObserver);
-        mModelValidator.assertCursorSize(root.getCursor(), startingCards.length);
-
-        // Append new children to root
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.builder().addCardsToRoot(appendedCards).build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
-        // assert the new state of the stream
-        mModelValidator.assertCursorSize(
-                root.getCursor(), startingCards.length + appendedCards.length);
-        ArgumentCaptor<FeatureChange> capture = ArgumentCaptor.forClass(FeatureChange.class);
-        verify(rootObserver).onChange(capture.capture());
-        List<FeatureChange> featureChanges = capture.getAllValues();
-        assertThat(featureChanges).hasSize(1);
-        FeatureChange change = featureChanges.get(0);
-        assertThat(change.getChildChanges().getAppendedChildren()).hasSize(appendedCards.length);
-        assertThat(change.isFeatureChanged()).isFalse();
-        int i = 0;
-        for (ModelChild appendedChild : change.getChildChanges().getAppendedChildren()) {
-            mModelValidator.assertStreamContentId(appendedChild.getContentId(),
-                    mProtocolAdapter.getStreamContentId(appendedCards[i++]));
-        }
-    }
-
-    @Test
-    public void appendChildren_concurrentModification() {
-        // Test which verifies the root can be updated while we advance a cursor (without
-        // a ConcurrentModificationException
-        // Create a simple stream with a root and two features
-        ContentId[] startingCards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2)};
-        // Define two features to be appended to the root
-        ContentId[] appendedCards = new ContentId[] {ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4)};
-
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(startingCards).build());
-        mRequestManager.triggerScheduledRefresh();
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        FeatureChangeObserver rootObserver = mock(FeatureChangeObserver.class);
-        root.registerObserver(rootObserver);
-        ModelCursor cursor = root.getCursor();
-        cursor.getNextItem();
-
-        // Now append additional children to the stream (and cursor)
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.builder().addCardsToRoot(appendedCards).build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        mModelValidator.assertCursorSize(cursor, 3);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SyntheticTokensTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SyntheticTokensTest.java
deleted file mode 100644
index f86d3291..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/SyntheticTokensTest.java
+++ /dev/null
@@ -1,492 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.ROOT_CONTENT_ID;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.api.host.logging.RequestReason;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.common.testing.ContentIdGenerators;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelChild;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelCursor;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.State;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelToken;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompleted;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.TokenCompletedObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.TaskQueue;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.PagingState;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-
-/** Test Synthetic tokens. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SyntheticTokensTest {
-    private static final int INITIAL_PAGE_SIZE = 4;
-    private static final int PAGE_SIZE = 4;
-    private static final int MIN_PAGE_SIZE = 2;
-
-    private FakeClock mFakeClock;
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private FeedSessionManager mFeedSessionManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private ProtocolAdapter mProtocolAdapter;
-    private final ContentIdGenerators mContentIdGenerators = new ContentIdGenerators();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        Configuration configuration =
-                new Configuration.Builder()
-                        .put(ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE, (long) INITIAL_PAGE_SIZE)
-                        .put(ConfigKey.NON_CACHED_PAGE_SIZE, (long) PAGE_SIZE)
-                        .put(ConfigKey.NON_CACHED_MIN_PAGE_SIZE, (long) MIN_PAGE_SIZE)
-                        .build();
-        InfraIntegrationScope scope =
-                new InfraIntegrationScope.Builder().setConfiguration(configuration).build();
-        mFakeClock = scope.getFakeClock();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mProtocolAdapter = scope.getProtocolAdapter();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-    }
-
-    /**
-     * This test will test the creation of synthetic tokens.
-     *
-     * <ol>
-     *   <li>Create an initial $HEAD with 13 items
-     *   <li>Clear the FeedSessionManager ContentCache to simulate non-cached mode
-     *   <li>Create a new session which will have a synthetic token at INITIAL_PAGE_SIZE
-     *   <li>FeedSessionManager.handleToken on the synthetic token, verify full cursor and partial
-     *       page cursor
-     *   <li>FeedSessionManager.handleToken on the next synthetic token, verify we get PAGE_SIZE +
-     *       slop. Verify both the full cursor and the partial page cursor. No further tokens will
-     * be in the cursor.
-     * </ol>
-     */
-    @Test
-    public void syntheticTokenPaging() {
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-                ResponseBuilder.createFeatureContentId(5),
-                ResponseBuilder.createFeatureContentId(6),
-                ResponseBuilder.createFeatureContentId(7),
-                ResponseBuilder.createFeatureContentId(9),
-                ResponseBuilder.createFeatureContentId(10),
-                ResponseBuilder.createFeatureContentId(11),
-                ResponseBuilder.createFeatureContentId(12),
-                ResponseBuilder.createFeatureContentId(13)};
-
-        // Create 13 cards (initial page size + page size + page size and slope of 1)
-        // Initial model will have all the cards
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        ModelChild token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOf(cards, INITIAL_PAGE_SIZE));
-        assertThat(token.getModelToken().isSynthetic()).isTrue();
-
-        // clear the ContentCache
-        clearSessionManagerContentCache();
-
-        // Create a new ModelProvider and verify the first page size is INITIAL_PAGE_SIZE (4)
-        modelProvider = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        cursor = root.getCursor();
-        token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOf(cards, INITIAL_PAGE_SIZE));
-
-        // Register observer, handle token, verify the full cursor and the observer's cursor
-        // This should be the second page (PAGE_SIZE) and there will still be a new synthetic token
-        TokenCompletedObserver tokenCompletedObserver = mock(TokenCompletedObserver.class);
-        token.getModelToken().registerObserver(tokenCompletedObserver);
-        modelProvider.handleToken(token.getModelToken());
-        ArgumentCaptor<TokenCompleted> completedArgumentCaptor =
-                ArgumentCaptor.forClass(TokenCompleted.class);
-        verify(tokenCompletedObserver).onTokenCompleted(completedArgumentCaptor.capture());
-        ModelCursor pageCursor = completedArgumentCaptor.getValue().getCursor();
-        assertThat(pageCursor).isNotNull();
-        mModelValidator.assertCursorContentsWithToken(cursor,
-                Arrays.copyOfRange(cards, INITIAL_PAGE_SIZE, INITIAL_PAGE_SIZE + PAGE_SIZE));
-        cursor = root.getCursor();
-        token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOf(cards, INITIAL_PAGE_SIZE + PAGE_SIZE));
-
-        // Register observer, handle token, verify that we pick up PAGE_SIZE + slop, verify
-        // observer cursor and full cursor, no further token will be in the cursor.
-        tokenCompletedObserver = mock(TokenCompletedObserver.class);
-        token.getModelToken().registerObserver(tokenCompletedObserver);
-        modelProvider.handleToken(token.getModelToken());
-        completedArgumentCaptor = ArgumentCaptor.forClass(TokenCompleted.class);
-        verify(tokenCompletedObserver).onTokenCompleted(completedArgumentCaptor.capture());
-        pageCursor = completedArgumentCaptor.getValue().getCursor();
-        assertThat(pageCursor).isNotNull();
-        mModelValidator.assertCursorContents(
-                pageCursor, Arrays.copyOfRange(cards, INITIAL_PAGE_SIZE + PAGE_SIZE, cards.length));
-        modelProvider.handleToken(token.getModelToken());
-        cursor = root.getCursor();
-        mModelValidator.assertCursorContents(cursor, cards);
-    }
-
-    /**
-     * This test will test the creation of synthetic tokens.
-     *
-     * <ol>
-     *   <li>Create an initial $HEAD with 13 items
-     *   <li>Clear the FeedSessionManager ContentCache to simulate non-cached mode
-     *   <li>Create a new session which will have a synthetic token at INITIAL_PAGE_SIZE
-     *   <li>FeedSessionManager.handleToken on the synthetic token, verify full cursor and partial
-     *       page cursor
-     *   <li>FeedSessionManager.handleToken on the next synthetic token, verify we get PAGE_SIZE +
-     *       slop. Verify both the full cursor and the partial page cursor. No further tokens will
-     * be in the cursor.
-     * </ol>
-     */
-    @Test
-    public void syntheticTokenPaging_withCache() {
-        ContentId[] cards = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-                ResponseBuilder.createFeatureContentId(5),
-                ResponseBuilder.createFeatureContentId(6),
-                ResponseBuilder.createFeatureContentId(7),
-                ResponseBuilder.createFeatureContentId(9),
-                ResponseBuilder.createFeatureContentId(10),
-                ResponseBuilder.createFeatureContentId(11),
-                ResponseBuilder.createFeatureContentId(12),
-                ResponseBuilder.createFeatureContentId(13)};
-
-        // Create 13 cards (initial page size + page size + page size and slope of 1)
-        // Initial model will have all the cards
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        ModelChild token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOfRange(cards, 0, INITIAL_PAGE_SIZE));
-        assertThat(token.getModelToken().isSynthetic()).isTrue();
-
-        // Create a new ModelProvider and verify the first page size is INITIAL_PAGE_SIZE (4)
-        modelProvider = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        cursor = root.getCursor();
-        token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOf(cards, INITIAL_PAGE_SIZE));
-
-        // Register observer, handle token, verify the full cursor and the observer's cursor
-        // This should be the second page (PAGE_SIZE) and there will still be a new synthetic token
-        TokenCompletedObserver tokenCompletedObserver = mock(TokenCompletedObserver.class);
-        token.getModelToken().registerObserver(tokenCompletedObserver);
-        modelProvider.handleToken(token.getModelToken());
-        ArgumentCaptor<TokenCompleted> completedArgumentCaptor =
-                ArgumentCaptor.forClass(TokenCompleted.class);
-        verify(tokenCompletedObserver).onTokenCompleted(completedArgumentCaptor.capture());
-        ModelCursor pageCursor = completedArgumentCaptor.getValue().getCursor();
-        assertThat(pageCursor).isNotNull();
-        mModelValidator.assertCursorContentsWithToken(cursor,
-                Arrays.copyOfRange(cards, INITIAL_PAGE_SIZE, INITIAL_PAGE_SIZE + PAGE_SIZE));
-        cursor = root.getCursor();
-        token = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOfRange(cards, 0, INITIAL_PAGE_SIZE + PAGE_SIZE));
-
-        // Register observer, handle token, verify that we pick up PAGE_SIZE + slop, verify
-        // observer cursor and full cursor, no further token will be in the cursor.
-        tokenCompletedObserver = mock(TokenCompletedObserver.class);
-        token.getModelToken().registerObserver(tokenCompletedObserver);
-        modelProvider.handleToken(token.getModelToken());
-        completedArgumentCaptor = ArgumentCaptor.forClass(TokenCompleted.class);
-        verify(tokenCompletedObserver).onTokenCompleted(completedArgumentCaptor.capture());
-        pageCursor = completedArgumentCaptor.getValue().getCursor();
-        assertThat(pageCursor).isNotNull();
-        mModelValidator.assertCursorContents(
-                pageCursor, Arrays.copyOfRange(cards, INITIAL_PAGE_SIZE + PAGE_SIZE, cards.length));
-        modelProvider.handleToken(token.getModelToken());
-        cursor = root.getCursor();
-        mModelValidator.assertCursorContents(cursor, cards);
-    }
-
-    /**
-     * Test Synthetic tokens on paged results.
-     *
-     * <ol>
-     *   <li>Create an initial $HEAD with 4 items and a second page of 8 additional items
-     *   <li>Create a new session and validate the expected cards and a real next page token.
-     *   <li>Handle the token to bring in the second page of items
-     *   <li>Validate that we have 8 total items, plus a synthetic token
-     *   <li>Handle the synthetic token
-     *   <li>Validate that we have all 12 total items with no token
-     * </ol>
-     */
-    @Test
-    public void syntheticTokenPaging_paging() {
-        ContentId[] cards = new ContentId[] {
-                ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-        };
-        ContentId[] pageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(5),
-                ResponseBuilder.createFeatureContentId(6),
-                ResponseBuilder.createFeatureContentId(7),
-                ResponseBuilder.createFeatureContentId(8),
-                ResponseBuilder.createFeatureContentId(9),
-                ResponseBuilder.createFeatureContentId(10),
-                ResponseBuilder.createFeatureContentId(11),
-                ResponseBuilder.createFeatureContentId(12)};
-
-        PagingState state = new PagingState(cards, pageCards, 1, mContentIdGenerators);
-        mFakeFeedRequestManager.queueResponse(state.initialResponse);
-        mFakeFeedRequestManager.queueResponse(state.pageResponse);
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        ModelChild tokenFeature = mModelValidator.assertCursorContentsWithToken(cursor, cards);
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isFalse();
-
-        ModelToken modelToken = tokenFeature.getModelToken();
-        modelProvider.handleToken(modelToken);
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, getExpectedCards(cards, pageCards, 4));
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isTrue();
-
-        modelProvider.handleToken(tokenFeature.getModelToken());
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, getExpectedCards(cards, pageCards, -1));
-        assertThat(tokenFeature).isNull();
-    }
-
-    /**
-     * Test Synthetic tokens on paged results when creating an existing session.
-     *
-     * <ol>
-     *   <li>Create an initial $HEAD with 4 items and a second page of 8 additional items
-     *   <li>Create a new session and validate the expected cards and a real next page token.
-     *   <li>Handle the token to bring in the second page of items
-     *   <li>Validate that we have 8 total items, plus a synthetic token
-     *   <li>Recreate the session (invalidating the first version)
-     *   <li>Validate that we have 4 cards and a synthetic token
-     *   <li>Handle the synthetic token
-     *   <li>Validate that we have 8 total items, plus a synthetic token
-     *   <li>Handle the synthetic token
-     *   <li>Validate that we have all 12 total items with no token
-     * </ol>
-     */
-    @Test
-    public void syntheticTokenPaging_pagingRestoredSession() {
-        ContentId[] cards = new ContentId[] {
-                ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-        };
-        ContentId[] pageCards = new ContentId[] {ResponseBuilder.createFeatureContentId(5),
-                ResponseBuilder.createFeatureContentId(6),
-                ResponseBuilder.createFeatureContentId(7),
-                ResponseBuilder.createFeatureContentId(8),
-                ResponseBuilder.createFeatureContentId(9),
-                ResponseBuilder.createFeatureContentId(10),
-                ResponseBuilder.createFeatureContentId(11),
-                ResponseBuilder.createFeatureContentId(12)};
-
-        PagingState state = new PagingState(cards, pageCards, 1, mContentIdGenerators);
-        mFakeFeedRequestManager.queueResponse(state.initialResponse);
-        mFakeFeedRequestManager.queueResponse(state.pageResponse);
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-        String sessionId = modelProvider.getSessionId();
-
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        ModelChild tokenFeature = mModelValidator.assertCursorContentsWithToken(cursor, cards);
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isFalse();
-
-        ModelToken modelToken = tokenFeature.getModelToken();
-        modelProvider.handleToken(modelToken);
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, getExpectedCards(cards, pageCards, 4));
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isTrue();
-
-        // now restore the session and see what we have
-        modelProvider = mModelProviderFactory.create(sessionId, UiContext.getDefaultInstance());
-        root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(cursor, cards);
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isTrue();
-
-        modelProvider.handleToken(tokenFeature.getModelToken());
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, getExpectedCards(cards, pageCards, 4));
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isTrue();
-
-        modelProvider.handleToken(tokenFeature.getModelToken());
-        cursor = root.getCursor();
-        tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, getExpectedCards(cards, pageCards, -1));
-        assertThat(tokenFeature).isNull();
-    }
-
-    /**
-     * This will tests synthetic tokens in the context of an empty ModelProvider, see [INTERNAL
-     * LINK]. This test is written directly against the session manager instead of the request
-     * manager.
-     *
-     * <p>
-     * <li>Create an initial Model provider with a single content item under the root
-     * <li>Remove that item, creating an empty Root
-     * <li>Added a new page of items with enough to cause a synthetic token to be added
-     */
-    @Test
-    public void testEmptyFeed() {
-        ContentId[] cards = new ContentId[] {
-                ResponseBuilder.createFeatureContentId(1),
-        };
-        ContentId[] cardsTwo = new ContentId[] {
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3),
-                ResponseBuilder.createFeatureContentId(4),
-                ResponseBuilder.createFeatureContentId(5),
-                ResponseBuilder.createFeatureContentId(6),
-                ResponseBuilder.createFeatureContentId(7),
-                ResponseBuilder.createFeatureContentId(8),
-        };
-
-        // Create an initial root with a single item in it.
-        mFakeFeedRequestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
-
-        // Prep for sending modifications to the model provider
-        MutationContext context = new MutationContext.Builder()
-                                          .setRequestingSessionId(modelProvider.getSessionId())
-                                          .build();
-
-        // Remove the single item under the root to create the empty state.
-        Result<Model> result = mProtocolAdapter.createModel(
-                ResponseBuilder.builder().removeFeature(cards[0], ROOT_CONTENT_ID).build());
-        assertThat(result.isSuccessful()).isTrue();
-        Consumer<Result<Model>> updateConsumer = mFeedSessionManager.getUpdateConsumer(context);
-        updateConsumer.accept(result);
-
-        // Verify the empty state
-        ModelFeature root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        ModelCursor cursor = root.getCursor();
-        assertThat(cursor.isAtEnd()).isTrue();
-
-        // Create a next page response with enough new cards to ensure a synthetic token is added
-        result = mProtocolAdapter.createModel(
-                ResponseBuilder.builder().addCardsToRoot(cardsTwo).build());
-        assertThat(result.isSuccessful()).isTrue();
-        updateConsumer = mFeedSessionManager.getUpdateConsumer(context);
-        updateConsumer.accept(result);
-
-        // Validate the current model
-        root = modelProvider.getRootFeature();
-        assertThat(root).isNotNull();
-        cursor = root.getCursor();
-        ModelChild tokenFeature = mModelValidator.assertCursorContentsWithToken(
-                cursor, Arrays.copyOf(cardsTwo, INITIAL_PAGE_SIZE));
-        assertThat(tokenFeature).isNotNull();
-        assertThat(tokenFeature.getModelToken().isSynthetic()).isTrue();
-    }
-
-    private ContentId[] getExpectedCards(ContentId[] first, ContentId[] second, int length) {
-        if (length == -1) {
-            length = second.length;
-        }
-        ContentId[] results = new ContentId[first.length + length];
-        System.arraycopy(first, 0, results, 0, first.length);
-        System.arraycopy(second, 0, results, first.length, length);
-        return results;
-    }
-
-    private void clearSessionManagerContentCache() {
-        // This is a bit of hack, which will clear the content cache and indicate to the
-        // FeedSessionManager that we are in the non-cached mode.
-        mFakeFeedRequestManager.queueResponse(new ResponseBuilder().build());
-        mFakeFeedRequestManager.triggerRefresh(RequestReason.OPEN_WITHOUT_CONTENT,
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-        mFakeClock.advance(TaskQueue.STARVATION_TIMEOUT_MS);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionBaseTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionBaseTest.java
deleted file mode 100644
index b1e4a537..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionBaseTest.java
+++ /dev/null
@@ -1,155 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedsessionmanager.FeedSessionManagerImpl;
-import org.chromium.chrome.browser.feed.library.testing.host.scheduler.FakeSchedulerApi;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * This is a TimeoutSession test which verifies the REQUEST_WITH_WAIT.
- *
- * <p>NOTE: This test has multiple threads running. There is a single threaded executor created in
- * addition to the main thread. The Test will throw a TimeoutException in in production in the event
- * of a deadlock. The DEBUG boolean controls the behavior to allow debugging.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TimeoutSessionBaseTest {
-    // This flag will should be flipped to debug the test.  It will disable TimeoutExceptions.
-    private static final boolean DEBUG = false;
-
-    private final FakeSchedulerApi mFakeSchedulerApi =
-            new FakeSchedulerApi(FakeThreadUtils.withoutThreadChecks());
-
-    private FakeClock mFakeClock;
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private FeedSessionManager mFeedSessionManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private long mTimeoutDeadline;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder()
-                                              .setSchedulerApi(mFakeSchedulerApi)
-                                              .withTimeoutSessionConfiguration(2L)
-                                              .build();
-        mFakeClock = scope.getFakeClock();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-    }
-
-    /**
-     * Test steps:
-     *
-     * <ol>
-     *   <li>Create the initial ModelProvider from $HEAD with a REQUEST_WITH_WAIT which makes the
-     *       request before the session is populated.
-     *   <li>Create a second ModelProvider using NO_REQUEST_WITH_CONTENT which should duplidate the
-     *       session created with the initial request.
-     * </ol>
-     */
-    @Test
-    public void testRequestWithWait() throws TimeoutException {
-        ContentId[] requestOne = new ContentId[] {ResponseBuilder.createFeatureContentId(1),
-                ResponseBuilder.createFeatureContentId(2),
-                ResponseBuilder.createFeatureContentId(3)};
-
-        // Load up the initial request
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(requestOne).build(), /* delayMs= */ 100);
-
-        // Wait for the request to complete (REQUEST_WITH_WAIT).  This will trigger the request and
-        // wait for it to complete to populate the new session.
-        mFakeSchedulerApi.setRequestBehavior(RequestBehavior.REQUEST_WITH_WAIT);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        // This will wait for the session to be created and validate the root cursor
-        AtomicBoolean finished = new AtomicBoolean(false);
-        assertSessionCreation(modelProvider, finished, requestOne);
-        long startTimeMs = mFakeClock.currentTimeMillis();
-        while (!finished.get()) {
-            // Loop through the tasks and wait for the assertSessionCreation to set finished to true
-            mFakeClock.tick();
-            if (mTimeoutDeadline > 0 && mFakeClock.currentTimeMillis() > mTimeoutDeadline) {
-                throw new TimeoutException();
-            }
-        }
-        assertThat(mFakeClock.currentTimeMillis() - startTimeMs).isAtLeast(100L);
-
-        // Create a new ModelProvider from HEAD (NO_REQUEST_WITH_CONTENT)
-        mFakeSchedulerApi.setRequestBehavior(RequestBehavior.NO_REQUEST_WITH_CONTENT);
-        // This will wait for the session to be created and validate the root cursor
-        modelProvider = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertSessionCreation(modelProvider, finished, requestOne);
-        startTimeMs = mFakeClock.currentTimeMillis();
-        while (!finished.get()) {
-            // Loop through the tasks and wait for the assertSessionCreation to set finished to true
-            mFakeClock.tick();
-            if (mTimeoutDeadline > 0 && mFakeClock.currentTimeMillis() > mTimeoutDeadline) {
-                throw new TimeoutException();
-            }
-        }
-        assertThat(mFakeClock.currentTimeMillis() - startTimeMs).isEqualTo(0);
-    }
-
-    private void assertSessionCreation(
-            ModelProvider modelProvider, AtomicBoolean finished, ContentId... cards) {
-        finished.set(false);
-        mTimeoutDeadline = DEBUG
-                ? InfraIntegrationScope.TIMEOUT_TEST_TIMEOUT + mFakeClock.currentTimeMillis()
-                : 0;
-        modelProvider.registerObserver(new ModelProviderObserver() {
-            @Override
-            public void onSessionStart(UiContext uiContext) {
-                System.out.println("onSessionStart");
-                finished.set(true);
-                mModelValidator.assertCursorContents(modelProvider, cards);
-                assertThat(((FeedSessionManagerImpl) mFeedSessionManager).isDelayed()).isFalse();
-            }
-
-            @Override
-            public void onSessionFinished(UiContext uiContext) {
-                System.out.println("onSessionFinished");
-            }
-
-            @Override
-            public void onError(ModelError modelError) {
-                System.out.println("onError");
-            }
-        });
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionWithContentTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionWithContentTest.java
deleted file mode 100644
index 0c9177c2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/TimeoutSessionWithContentTest.java
+++ /dev/null
@@ -1,203 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder.ROOT_CONTENT_ID;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.FeatureChange.ChildChanges;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelFeature;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderObserver;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.host.scheduler.FakeSchedulerApi;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * This is a TimeoutSession test which verifies REQUEST_WITH_CONTENT
- *
- * <p>NOTE: This test has multiple threads running. There is a single threaded executor created in
- * addition to the main thread. The Test will throw a TimeoutException in in production in the event
- * of a deadlock. The DEBUG boolean controls the behavior to allow debugging.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TimeoutSessionWithContentTest {
-    // This flag will should be flipped to debug the test.  It will disable TimeoutExceptions.
-    private static final boolean DEBUG = false;
-    private static final ContentId[] REQUEST_ONE = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3)};
-    private static final ContentId[] REQUEST_TWO = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(4), ResponseBuilder.createFeatureContentId(3),
-            ResponseBuilder.createFeatureContentId(5)};
-
-    private final FakeSchedulerApi mFakeSchedulerApi =
-            new FakeSchedulerApi(FakeThreadUtils.withoutThreadChecks());
-
-    private FakeClock mFakeClock;
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private ModelProviderFactory mModelProviderFactory;
-    private ProtocolAdapter mProtocolAdapter;
-    private ModelProviderValidator mModelValidator;
-    private long mTimeoutDeadline;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder()
-                                              .setSchedulerApi(mFakeSchedulerApi)
-                                              .withTimeoutSessionConfiguration(2L)
-                                              .build();
-        mFakeClock = scope.getFakeClock();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        mProtocolAdapter = scope.getProtocolAdapter();
-        mModelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
-    }
-
-    /**
-     * Test steps:
-     *
-     * <ol>
-     *   <li>Create the initial ModelProvider from $HEAD with a REQUEST_WITH_WAIT which makes the
-     *       request before the session is populated.
-     *   <li>Load the second request into the FeedRequestManager
-     *   <li>Create a second ModelProvider using REQUEST_WITH_CONTENT which displays the initial
-     * $HEAD but makes a request. The second request will be appended to and update the
-     * ModelProvider.
-     * </ol>
-     */
-    @Test
-    public void testRequestWithWait() throws TimeoutException {
-        // Load up the initial request
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_ONE).build(), /* delayMs= */ 100);
-
-        // Wait for the request to complete (REQUEST_WITH_CONTENT).  This will trigger the request
-        // and wait for it to complete to populate the new session.
-        mFakeSchedulerApi.setRequestBehavior(RequestBehavior.REQUEST_WITH_WAIT);
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-
-        // This will wait for the session to be created and validate the root cursor
-        AtomicBoolean finished = new AtomicBoolean(false);
-        assertSessionCreation(modelProvider, finished);
-        long startTimeMs = mFakeClock.currentTimeMillis();
-        while (!finished.get()) {
-            // Loop through the tasks and wait for the assertSessionCreation to set finished to true
-            mFakeClock.tick();
-            if (mTimeoutDeadline > 0 && mFakeClock.currentTimeMillis() > mTimeoutDeadline) {
-                throw new TimeoutException();
-            }
-        }
-        assertThat(mFakeClock.currentTimeMillis() - startTimeMs).isAtLeast(100L);
-
-        // Create a new ModelProvider from HEAD (REQUEST_WITH_CONTENT)
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_TWO).build(), /* delayMs= */ 100);
-        mFakeSchedulerApi.setRequestBehavior(RequestBehavior.REQUEST_WITH_CONTENT);
-        // This will wait for the session to be created and validate the root cursor
-        modelProvider = mModelProviderFactory.createNew(null, UiContext.getDefaultInstance());
-        assertSessionCreationWithRequest(modelProvider, finished);
-        startTimeMs = mFakeClock.currentTimeMillis();
-        while (!finished.get()) {
-            // Loop through the tasks and wait for the assertSessionCreation to set finished to true
-            mFakeClock.tick();
-            if (mTimeoutDeadline > 0 && mFakeClock.currentTimeMillis() > mTimeoutDeadline) {
-                throw new TimeoutException();
-            }
-        }
-        assertThat(mFakeClock.currentTimeMillis() - startTimeMs).isAtLeast(100L);
-    }
-
-    // Verifies the initial session.
-    private void assertSessionCreation(ModelProvider modelProvider, AtomicBoolean finished) {
-        finished.set(false);
-        mTimeoutDeadline = DEBUG
-                ? InfraIntegrationScope.TIMEOUT_TEST_TIMEOUT + mFakeClock.currentTimeMillis()
-                : 0;
-        modelProvider.registerObserver(new ModelProviderObserver() {
-            @Override
-            public void onSessionStart(UiContext uiContext) {
-                System.out.println("onSessionStart");
-                finished.set(true);
-                mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE);
-            }
-
-            @Override
-            public void onSessionFinished(UiContext uiContext) {
-                System.out.println("onSessionFinished");
-            }
-
-            @Override
-            public void onError(ModelError modelError) {
-                System.out.println("onError");
-            }
-        });
-    }
-
-    // Verifies the second session.  There are two observers verified, the ModelProvider READ
-    // and a change listener on the root for the second request.
-    private void assertSessionCreationWithRequest(
-            ModelProvider modelProvider, AtomicBoolean finished) {
-        finished.set(false);
-        mTimeoutDeadline = DEBUG
-                ? InfraIntegrationScope.TIMEOUT_TEST_TIMEOUT + mFakeClock.currentTimeMillis()
-                : 0;
-        modelProvider.registerObserver(new ModelProviderObserver() {
-            @Override
-            public void onSessionStart(UiContext uiContext) {
-                System.out.println("onSessionStart");
-                ModelFeature feature = modelProvider.getRootFeature();
-                // The second request will cause an change on the root
-                assertThat(feature).isNotNull();
-                feature.registerObserver(change -> {
-                    System.out.println("root.onChange");
-                    finished.set(true);
-                    mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE[0],
-                            REQUEST_ONE[1], REQUEST_ONE[2], REQUEST_TWO[0], REQUEST_TWO[2]);
-                    assertThat(change.getContentId())
-                            .isEqualTo(mProtocolAdapter.getStreamContentId(ROOT_CONTENT_ID));
-                    ChildChanges childChanges = change.getChildChanges();
-                    assertThat(childChanges.getAppendedChildren().size()).isEqualTo(2);
-                });
-                mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE);
-            }
-
-            @Override
-            public void onSessionFinished(UiContext uiContext) {
-                System.out.println("onSessionFinished");
-            }
-
-            @Override
-            public void onError(ModelError modelError) {
-                System.out.println("onError");
-            }
-        });
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ViewDepthProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ViewDepthProviderTest.java
deleted file mode 100644
index 81c22b2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ViewDepthProviderTest.java
+++ /dev/null
@@ -1,152 +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.
-
-package org.chromium.chrome.browser.feed.library.infraintegration;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.requestmanager.RequestManager;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.RequestBehavior;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi.SessionState;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProviderFactory;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.api.internal.sessionmanager.FeedSessionManager;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.testing.InfraIntegrationScope;
-import org.chromium.chrome.browser.feed.library.common.testing.ModelProviderValidator;
-import org.chromium.chrome.browser.feed.library.common.testing.ResponseBuilder;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeViewDepthProvider;
-import org.chromium.chrome.browser.feed.library.testing.requestmanager.FakeFeedRequestManager;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.UiContext;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests of the ViewDepthProvider. The ViewDepthProvider indicates the depth of the Stream the user
- * has seen. For some types of SchedulerApi {@link RequestBehavior} values this will prune the
- * Stream beyond that depth.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ViewDepthProviderTest {
-    private static final ContentId[] REQUEST_ONE = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2),
-            ResponseBuilder.createFeatureContentId(3), ResponseBuilder.createFeatureContentId(4)};
-    private static final ContentId[] REQUEST_TWO = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(5), ResponseBuilder.createFeatureContentId(6),
-            ResponseBuilder.createFeatureContentId(7)};
-    private static final ContentId[] REQUEST_TWO_WITH_DUPLICATES = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(5), ResponseBuilder.createFeatureContentId(6),
-            ResponseBuilder.createFeatureContentId(4)};
-    private static final ContentId[] REQUEST_TWO_WITH_DUPLICATES_PAGE = new ContentId[] {
-            ResponseBuilder.createFeatureContentId(5), ResponseBuilder.createFeatureContentId(4),
-            ResponseBuilder.createFeatureContentId(7)};
-
-    @Mock
-    private SchedulerApi mSchedulerApi;
-
-    private FakeFeedRequestManager mFakeFeedRequestManager;
-    private FakeThreadUtils mFakeThreadUtils;
-    private ModelProviderFactory mModelProviderFactory;
-    private ModelProviderValidator mModelValidator;
-    private FeedSessionManager mFeedSessionManager;
-    private ViewDepthProvider mViewDepthProvider;
-    private RequestManager mRequestManager;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        InfraIntegrationScope scope = new InfraIntegrationScope.Builder()
-                                              .setSchedulerApi(mSchedulerApi)
-                                              .withTimeoutSessionConfiguration(2L)
-                                              .build();
-        mFakeThreadUtils = scope.getFakeThreadUtils();
-        mFakeFeedRequestManager = scope.getFakeFeedRequestManager();
-        mModelProviderFactory = scope.getModelProviderFactory();
-        ProtocolAdapter protocolAdapter = scope.getProtocolAdapter();
-        mModelValidator = new ModelProviderValidator(protocolAdapter);
-        mFeedSessionManager = scope.getFeedSessionManager();
-        mViewDepthProvider = new FakeViewDepthProvider().setChildViewDepth(
-                protocolAdapter.getStreamContentId(REQUEST_ONE[1]));
-        mRequestManager = scope.getRequestManager();
-    }
-
-    @Test
-    public void baseDepthProviderTest() {
-        // Load up the initial request
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_ONE).build());
-
-        // The REQUEST_ONE content will be added to head, this is then used to create the initial
-        // session.
-        mRequestManager.triggerScheduledRefresh();
-
-        // The REQUEST_TWO content acts as a second request on the server, it is triggered by
-        // REQUEST_WITH_CONTENT
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.REQUEST_WITH_CONTENT);
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_TWO).build());
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(mViewDepthProvider, UiContext.getDefaultInstance());
-
-        // The second request will be added after the first request, the ViewDepthProvider indicates
-        // we only saw [0] and [1] from the first request, so [2] and [3] will be removed.
-        mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE[0], REQUEST_ONE[1],
-                REQUEST_TWO[0], REQUEST_TWO[1], REQUEST_TWO[2]);
-    }
-
-    @Test
-    public void testDuplicateEntries() {
-        // Load up the initial request
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_ONE).build());
-
-        // The REQUEST_ONE content will be added to head, this is then used to create the initial
-        // session.
-        mRequestManager.triggerScheduledRefresh();
-
-        // The REQUEST_TWO content acts as a second request on the server, it is triggered by
-        // REQUEST_WITH_CONTENT
-        when(mSchedulerApi.shouldSessionRequestData(any(SessionState.class)))
-                .thenReturn(RequestBehavior.REQUEST_WITH_CONTENT);
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.forClearAllWithCards(REQUEST_TWO_WITH_DUPLICATES).build());
-        ModelProvider modelProvider =
-                mModelProviderFactory.createNew(mViewDepthProvider, UiContext.getDefaultInstance());
-
-        // The second request will be added after the first request, the ViewDepthProvider indicates
-        // we only saw [0] and [1] from the first request, so [2] and [3] will be removed.
-        mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE[0], REQUEST_ONE[1],
-                REQUEST_TWO_WITH_DUPLICATES[0], REQUEST_TWO_WITH_DUPLICATES[1],
-                REQUEST_TWO_WITH_DUPLICATES[2]);
-
-        // Now page in the same content, this should all be updates
-        mFakeFeedRequestManager.queueResponse(
-                ResponseBuilder.builder().addCardsToRoot(REQUEST_TWO_WITH_DUPLICATES_PAGE).build());
-        // TODO: sessions reject updates without a CLEAR_ALL or paging with a different token.
-        mFakeThreadUtils.enforceMainThread(false);
-        mFakeFeedRequestManager.loadMore(StreamToken.getDefaultInstance(),
-                ConsistencyToken.getDefaultInstance(),
-                mFeedSessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
-        mModelValidator.assertCursorContents(modelProvider, REQUEST_ONE[0], REQUEST_ONE[1],
-                REQUEST_TWO_WITH_DUPLICATES[0], REQUEST_TWO_WITH_DUPLICATES[1],
-                REQUEST_TWO_WITH_DUPLICATES[2], REQUEST_TWO_WITH_DUPLICATES_PAGE[2]);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/mocknetworkclient/MockServerNetworkClientTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/mocknetworkclient/MockServerNetworkClientTest.java
deleted file mode 100644
index c753fcc..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/mocknetworkclient/MockServerNetworkClientTest.java
+++ /dev/null
@@ -1,262 +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.
-
-package org.chromium.chrome.browser.feed.library.mocknetworkclient;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.net.Uri;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedInputStream;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.feed.library.api.host.config.ApplicationInfo;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpRequest.HttpMethod;
-import org.chromium.chrome.browser.feed.library.api.host.network.HttpResponse;
-import org.chromium.chrome.browser.feed.library.api.host.scheduler.SchedulerApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.TooltipSupportedApi;
-import org.chromium.chrome.browser.feed.library.api.internal.actionmanager.ActionReader;
-import org.chromium.chrome.browser.feed.library.api.internal.common.Model;
-import org.chromium.chrome.browser.feed.library.api.internal.protocoladapter.ProtocolAdapter;
-import org.chromium.chrome.browser.feed.library.common.Result;
-import org.chromium.chrome.browser.feed.library.common.concurrent.MainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeTaskQueue;
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeThreadUtils;
-import org.chromium.chrome.browser.feed.library.common.protoextensions.FeedExtensionRegistry;
-import org.chromium.chrome.browser.feed.library.common.time.TimingUtils;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.feedrequestmanager.FeedRequestManagerImpl;
-import org.chromium.chrome.browser.feed.library.testing.conformance.network.NetworkClientConformanceTest;
-import org.chromium.chrome.browser.feed.library.testing.host.logging.FakeBasicLoggingApi;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProviderJni;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamToken;
-import org.chromium.components.feed.core.proto.wire.ConsistencyTokenProto.ConsistencyToken;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response;
-import org.chromium.components.feed.core.proto.wire.ResponseProto.Response.ResponseVersion;
-import org.chromium.components.feed.core.proto.wire.mockserver.MockServerProto.ConditionalResponse;
-import org.chromium.components.feed.core.proto.wire.mockserver.MockServerProto.MockServer;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-
-/** Tests of the {@link MockServerNetworkClient} class. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-@Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)
-@Features.
-DisableFeatures({ChromeFeatureList.REPORT_FEED_USER_ACTIONS, ChromeFeatureList.INTEREST_FEED_V2})
-public class MockServerNetworkClientTest extends NetworkClientConformanceTest {
-    private final Configuration mConfiguration = new Configuration.Builder().build();
-    private final FakeClock mFakeClock = new FakeClock();
-    private final FakeThreadUtils mFakeThreadUtils = FakeThreadUtils.withThreadChecks();
-    private final FeedExtensionRegistry mExtensionRegistry =
-            new FeedExtensionRegistry(ArrayList::new);
-    private final TimingUtils mTimingUtils = new TimingUtils();
-
-    @Mock
-    private ActionReader mActionReader;
-    @Mock
-    private ProtocolAdapter mProtocolAdapter;
-    @Mock
-    private SchedulerApi mScheduler;
-    @Mock
-    private TooltipSupportedApi mTooltipSupportedApi;
-    @Mock
-    private IdentityServicesProvider.Natives mIdentityServicesProviderJniMock;
-    @Mock
-    private Profile mProfileMock;
-    @Mock
-    private IdentityManager mIdentifiyManagerMock;
-    @Captor
-    private ArgumentCaptor<Response> mResponseCaptor;
-    private ApplicationInfo mApplicationInfo;
-    private Context mContext;
-    private FakeBasicLoggingApi mBasicLoggingApi;
-    private MainThreadRunner mMainThreadRunner;
-
-    @Rule
-    public JniMocker jniMocker = new JniMocker();
-
-    @Rule
-    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
-
-    @Override
-    protected Uri getValidUri(@HttpMethod String method) {
-        // The URI does not matter - mockNetworkClient will default to an empty response
-        return new Uri.Builder().path("foo").appendPath(method).build();
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mApplicationInfo = new ApplicationInfo.Builder(mContext).setVersionString("0").build();
-        mMainThreadRunner = FakeMainThreadRunner.runTasksImmediately();
-
-        MockServer mockServer = MockServer.getDefaultInstance();
-        mNetworkClient =
-                new MockServerNetworkClient(mContext, mockServer, /* responseDelayMillis= */ 0L);
-
-        when(mActionReader.getDismissActionsWithSemanticProperties())
-                .thenReturn(Result.success(Collections.emptyList()));
-
-        mBasicLoggingApi = new FakeBasicLoggingApi();
-
-        Profile.setLastUsedProfileForTesting(mProfileMock);
-        jniMocker.mock(IdentityServicesProviderJni.TEST_HOOKS, mIdentityServicesProviderJniMock);
-        when(mIdentityServicesProviderJniMock.getIdentityManager(mProfileMock))
-                .thenReturn(mIdentifiyManagerMock);
-    }
-
-    @After
-    public void tearDown() {
-        Profile.setLastUsedProfileForTesting(null);
-    }
-
-    @Test
-    public void testSend() {
-        MockServer.Builder mockServerBuilder = MockServer.newBuilder();
-        Response initialResponse =
-                Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build();
-        mockServerBuilder.setInitialResponse(initialResponse);
-        MockServerNetworkClient networkClient = new MockServerNetworkClient(
-                mContext, mockServerBuilder.build(), /* responseDelayMillis= */ 0L);
-        Consumer<HttpResponse> responseConsumer = input -> {
-            try {
-                CodedInputStream inputStream =
-                        CodedInputStream.newInstance(input.getResponseBody());
-                int length = inputStream.readRawVarint32();
-                assertThat(inputStream.readRawBytes(length))
-                        .isEqualTo(initialResponse.toByteArray());
-                assertThat(input.getResponseCode()).isEqualTo(200);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-        };
-        networkClient.send(
-                new HttpRequest(Uri.EMPTY, HttpMethod.POST, Collections.emptyList(), new byte[] {}),
-                responseConsumer);
-    }
-
-    @Test
-    public void testSend_oneTimeResponse() {
-        MockServer.Builder mockServerBuilder = MockServer.newBuilder();
-        Response initialResponse =
-                Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build();
-        mockServerBuilder.setInitialResponse(initialResponse);
-        MockServerNetworkClient networkClient = new MockServerNetworkClient(
-                mContext, mockServerBuilder.build(), /* responseDelayMillis= */ 0L);
-        Consumer<HttpResponse> responseConsumer = input -> {
-            try {
-                CodedInputStream inputStream =
-                        CodedInputStream.newInstance(input.getResponseBody());
-                int length = inputStream.readRawVarint32();
-                assertThat(inputStream.readRawBytes(length))
-                        .isEqualTo(initialResponse.toByteArray());
-                assertThat(input.getResponseCode()).isEqualTo(200);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-        };
-        networkClient.send(
-                new HttpRequest(Uri.EMPTY, HttpMethod.POST, Collections.emptyList(), new byte[] {}),
-                responseConsumer);
-    }
-
-    @Test
-    public void testPaging() {
-        MockServer.Builder mockServerBuilder = MockServer.newBuilder();
-        Response response =
-                Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build();
-        ByteString token = ByteString.copyFromUtf8("fooToken");
-        StreamToken streamToken = StreamToken.newBuilder().setNextPageToken(token).build();
-        ConditionalResponse.Builder conditionalResponseBuilder = ConditionalResponse.newBuilder();
-        conditionalResponseBuilder.setContinuationToken(token).setResponse(response);
-        mockServerBuilder.addConditionalResponses(conditionalResponseBuilder.build());
-        MockServerNetworkClient networkClient = new MockServerNetworkClient(
-                mContext, mockServerBuilder.build(), /* responseDelayMillis= */ 0L);
-        FeedRequestManagerImpl feedRequestManager = new FeedRequestManagerImpl(mConfiguration,
-                networkClient, mProtocolAdapter, mExtensionRegistry, mScheduler, getTaskQueue(),
-                mTimingUtils, mFakeThreadUtils, mActionReader, mContext, mApplicationInfo,
-                mMainThreadRunner, mBasicLoggingApi, mTooltipSupportedApi);
-        when(mProtocolAdapter.createModel(any(Response.class)))
-                .thenReturn(Result.success(Model.empty()));
-
-        mFakeThreadUtils.enforceMainThread(false);
-        feedRequestManager.loadMore(streamToken, ConsistencyToken.getDefaultInstance(), result -> {
-            assertThat(result.isSuccessful()).isTrue();
-            assertThat(result.getValue().streamDataOperations).hasSize(0);
-        });
-
-        verify(mProtocolAdapter).createModel(mResponseCaptor.capture());
-        assertThat(mResponseCaptor.getValue()).isEqualTo(response);
-    }
-
-    @Test
-    public void testPaging_noMatch() {
-        MockServer.Builder mockServerBuilder = MockServer.newBuilder();
-        Response response =
-                Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build();
-        // Create a MockServerConfig without a matching token.
-        ConditionalResponse.Builder conditionalResponseBuilder = ConditionalResponse.newBuilder();
-        conditionalResponseBuilder.setResponse(response);
-        mockServerBuilder.addConditionalResponses(conditionalResponseBuilder.build());
-        MockServerNetworkClient networkClient = new MockServerNetworkClient(
-                mContext, mockServerBuilder.build(), /* responseDelayMillis= */ 0L);
-        FeedRequestManagerImpl feedRequestManager = new FeedRequestManagerImpl(mConfiguration,
-                networkClient, mProtocolAdapter, mExtensionRegistry, mScheduler, getTaskQueue(),
-                mTimingUtils, mFakeThreadUtils, mActionReader, mContext, mApplicationInfo,
-                mMainThreadRunner, mBasicLoggingApi, mTooltipSupportedApi);
-        when(mProtocolAdapter.createModel(any(Response.class)))
-                .thenReturn(Result.success(Model.empty()));
-
-        mFakeThreadUtils.enforceMainThread(false);
-        ByteString token = ByteString.copyFromUtf8("fooToken");
-        StreamToken streamToken = StreamToken.newBuilder().setNextPageToken(token).build();
-        feedRequestManager.loadMore(streamToken, ConsistencyToken.getDefaultInstance(), result -> {
-            assertThat(result.isSuccessful()).isTrue();
-            assertThat(result.getValue().streamDataOperations).hasSize(0);
-        });
-
-        verify(mProtocolAdapter).createModel(mResponseCaptor.capture());
-        assertThat(mResponseCaptor.getValue()).isEqualTo(Response.getDefaultInstance());
-    }
-
-    private FakeTaskQueue getTaskQueue() {
-        FakeTaskQueue taskQueue = new FakeTaskQueue(mFakeClock, mFakeThreadUtils);
-        taskQueue.initialize(() -> {});
-        return taskQueue;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ChunkedTextElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ChunkedTextElementAdapterTest.java
deleted file mode 100644
index 16e38e2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ChunkedTextElementAdapterTest.java
+++ /dev/null
@@ -1,892 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.api.host.imageloader.ImageLoaderApi.DIMENSION_UNKNOWN;
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.library.piet.ChunkedTextElementAdapter.SINGLE_LAYER_ID;
-import static org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode.ERR_MISSING_BINDING_VALUE;
-import static org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode.ERR_MISSING_OR_UNHANDLED_CONTENT;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.text.SpannableStringBuilder;
-import android.text.SpannedString;
-import android.text.style.AbsoluteSizeSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.ImageSpan;
-import android.text.style.StyleSpan;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowTextView;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.AdapterFactory.SingletonKeySupplier;
-import org.chromium.chrome.browser.feed.library.piet.ChunkedTextElementAdapter.ActionsClickableSpan;
-import org.chromium.chrome.browser.feed.library.piet.ChunkedTextElementAdapter.ImageSpanDrawableCallback;
-import org.chromium.chrome.browser.feed.library.piet.ChunkedTextElementAdapterTest.ShadowTextViewWithHeight;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Actions;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ActionsBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ChunkedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ImageBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.ImageSource;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.Chunk;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ChunkedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.StyledImageChunk;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.StyledTextChunk;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ChunkedTextElementAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = ShadowTextViewWithHeight.class)
-public class ChunkedTextElementAdapterTest {
-    private static final StyleIdsStack CHUNK_STYLE =
-            StyleIdsStack.newBuilder().addStyleIds("roasted").build();
-    private static final String CHUNKY_TEXT = "Skippy";
-    private static final String PROCESSED_TEXT = "smooth";
-    private static final ParameterizedText PARAMETERIZED_CHUNK_TEXT =
-            ParameterizedText.newBuilder().setText(CHUNKY_TEXT).build();
-    private static final Chunk TEXT_CHUNK =
-            Chunk.newBuilder()
-                    .setTextChunk(StyledTextChunk.newBuilder()
-                                          .setParameterizedText(PARAMETERIZED_CHUNK_TEXT)
-                                          .setStyleReferences(CHUNK_STYLE))
-                    .build();
-    private static final ChunkedText CHUNKED_TEXT_TEXT =
-            ChunkedText.newBuilder().addChunks(TEXT_CHUNK).build();
-    private static final String CHUNKY_URL = "pb.com/jif";
-    private static final Image IMAGE_CHUNK_IMAGE =
-            Image.newBuilder().addSources(ImageSource.newBuilder().setUrl(CHUNKY_URL)).build();
-    private static final Chunk IMAGE_CHUNK =
-            Chunk.newBuilder()
-                    .setImageChunk(StyledImageChunk.newBuilder()
-                                           .setImage(IMAGE_CHUNK_IMAGE)
-                                           .setStyleReferences(CHUNK_STYLE))
-                    .build();
-    private static final String BINDING_ID = "PB";
-    private static final ChunkedTextBindingRef CHUNKED_TEXT_BINDING_REF =
-            ChunkedTextBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-    private static final int STYLE_HEIGHT_PX = 6;
-    private static final int STYLE_ASPECT_RATIO = 2;
-    private static final int STYLE_WIDTH_PX = STYLE_HEIGHT_PX * STYLE_ASPECT_RATIO;
-
-    private static final int HEIGHT_DP = 6;
-    private static final int WIDTH_DP = HEIGHT_DP * STYLE_ASPECT_RATIO;
-
-    private static final int IMAGE_HEIGHT_PX = 60;
-    private static final int IMAGE_ASPECT_RATIO = 3;
-    private static final int IMAGE_WIDTH_PX = IMAGE_HEIGHT_PX * IMAGE_ASPECT_RATIO;
-
-    private static final int TEXT_HEIGHT = 12;
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private StyleProvider mMockStyleProvider;
-    @Mock
-    private ParameterizedTextEvaluator mMockTextEvaluator;
-    @Mock
-    private AssetProvider mMockAssetProvider;
-    @Mock
-    private ActionHandler mMockActionHandler;
-    @Mock
-    private HostProviders mHostProviders;
-
-    private Drawable mDrawable;
-    private SpannableStringBuilder mSpannable;
-
-    private Context mContext;
-    private TextView mTextView;
-
-    private AdapterParameters mAdapterParameters;
-
-    private ChunkedTextElementAdapter mAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        mDrawable = new BitmapDrawable(
-                Bitmap.createBitmap(IMAGE_WIDTH_PX, IMAGE_HEIGHT_PX, Bitmap.Config.ARGB_8888));
-        Shadows.shadowOf(mDrawable).setIntrinsicHeight(IMAGE_HEIGHT_PX);
-        Shadows.shadowOf(mDrawable).setIntrinsicWidth(IMAGE_WIDTH_PX);
-        mSpannable = new SpannableStringBuilder();
-
-        mTextView = new TextView(mContext);
-
-        when(mMockTextEvaluator.evaluate(mMockAssetProvider,
-                     ParameterizedText.newBuilder().setText(CHUNKY_TEXT).build()))
-                .thenReturn(PROCESSED_TEXT);
-        when(mFrameContext.makeStyleFor(CHUNK_STYLE)).thenReturn(mMockStyleProvider);
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
-                .thenAnswer(invocation -> invocation.getArguments()[0]);
-        when(mFrameContext.reportMessage(anyInt(), any(), anyString()))
-                .thenAnswer(invocation -> invocation.getArguments()[2]);
-        when(mHostProviders.getAssetProvider()).thenReturn(mMockAssetProvider);
-        when(mMockAssetProvider.isRtL()).thenReturn(false);
-        when(mFrameContext.getActionHandler()).thenReturn(mMockActionHandler);
-        when(mMockStyleProvider.getFont()).thenReturn(Font.getDefaultInstance());
-        when(mMockStyleProvider.getMargins()).thenReturn(EdgeWidths.getDefaultInstance());
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(DIMENSION_UNKNOWN);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(DIMENSION_UNKNOWN);
-
-        mAdapterParameters = new AdapterParameters(
-                null, null, mHostProviders, mMockTextEvaluator, null, null, new FakeClock());
-
-        when(mFrameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .thenReturn(mAdapterParameters.mDefaultStyleProvider);
-
-        mAdapter = new ChunkedTextElementAdapter.KeySupplier().getAdapter(
-                mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testCreate() {
-        assertThat(mAdapter).isNotNull();
-    }
-
-    @Test
-    public void testBind_chunkedText() {
-        TextElement chunkedTextElement =
-                TextElement.newBuilder().setChunkedText(CHUNKED_TEXT_TEXT).build();
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(PROCESSED_TEXT);
-    }
-
-    @Test
-    public void testBind_chunkedTextBinding() {
-        TextElement chunkedTextBindingElement =
-                TextElement.newBuilder().setChunkedTextBinding(CHUNKED_TEXT_BINDING_REF).build();
-
-        when(mFrameContext.getChunkedTextBindingValue(CHUNKED_TEXT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setChunkedText(CHUNKED_TEXT_TEXT).build());
-
-        mAdapter.createAdapter(asElement(chunkedTextBindingElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextBindingElement), mFrameContext);
-
-        verify(mFrameContext).getChunkedTextBindingValue(CHUNKED_TEXT_BINDING_REF);
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(PROCESSED_TEXT);
-    }
-
-    @Test
-    public void testBind_chunkedTextWithBoundText() {
-        ParameterizedTextBindingRef textBinding =
-                ParameterizedTextBindingRef.newBuilder().setBindingId("sometext").build();
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setTextChunk(
-                                        StyledTextChunk.newBuilder().setParameterizedTextBinding(
-                                                textBinding))))
-                        .build();
-        when(mFrameContext.getParameterizedTextBindingValue(textBinding))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setParameterizedText(PARAMETERIZED_CHUNK_TEXT)
-                                    .build());
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(PROCESSED_TEXT);
-    }
-
-    @Test
-    public void testBind_chunkedTextWithBoundImage() {
-        ImageBindingRef imageBinding =
-                ImageBindingRef.newBuilder().setBindingId("image-binding-id").build();
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setImageChunk(
-                                        StyledImageChunk.newBuilder().setImageBinding(
-                                                imageBinding))))
-                        .build();
-        when(mFrameContext.getImageBindingValue(imageBinding))
-                .thenReturn(BindingValue.newBuilder().setImage(IMAGE_CHUNK_IMAGE).build());
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(" ");
-        ImageSpan[] imageSpans =
-                ((SpannedString) mAdapter.getBaseView().getText()).getSpans(0, 1, ImageSpan.class);
-        assertThat(imageSpans).hasLength(1);
-
-        LayerDrawable containerDrawable = (LayerDrawable) imageSpans[0].getDrawable();
-
-        ArgumentCaptor<ImageSpanDrawableCallback> imageCallbackCaptor =
-                ArgumentCaptor.forClass(ImageSpanDrawableCallback.class);
-
-        verify(mMockAssetProvider)
-                .getImage(eq(IMAGE_CHUNK_IMAGE), eq(DIMENSION_UNKNOWN), eq(DIMENSION_UNKNOWN),
-                        imageCallbackCaptor.capture());
-
-        // Activate the image loading callback
-        Drawable imageDrawable = new ColorDrawable(123);
-        imageCallbackCaptor.getValue().accept(imageDrawable);
-
-        // Assert that we set the image on the span
-        assertThat(containerDrawable.getDrawable(SINGLE_LAYER_ID)).isSameInstanceAs(imageDrawable);
-    }
-
-    @Test
-    public void testBind_styledTextChunkWithNoContent() {
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setTextChunk(
-                                        StyledTextChunk.getDefaultInstance())))
-                        .build();
-        when(mMockTextEvaluator.evaluate(
-                     mMockAssetProvider, ParameterizedText.getDefaultInstance()))
-                .thenReturn("");
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEmpty();
-        verify(mFrameContext)
-                .reportMessage(MessageType.ERROR, ERR_MISSING_OR_UNHANDLED_CONTENT,
-                        "StyledTextChunk missing ParameterizedText content; has CONTENT_NOT_SET");
-    }
-
-    @Test
-    public void testBind_chunkedTextWithOptionalBindingNoImage() {
-        ImageBindingRef imageBinding = ImageBindingRef.newBuilder()
-                                               .setBindingId("image-binding-id")
-                                               .setIsOptional(true)
-                                               .build();
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setImageChunk(
-                                        StyledImageChunk.newBuilder().setImageBinding(
-                                                imageBinding))))
-                        .build();
-        when(mFrameContext.getImageBindingValue(imageBinding))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEmpty();
-        ImageSpan[] imageSpans =
-                ((SpannedString) mAdapter.getBaseView().getText()).getSpans(0, 1, ImageSpan.class);
-        assertThat(imageSpans).hasLength(0);
-        verify(mFrameContext, never()).reportMessage(anyInt(), any(ErrorCode.class), anyString());
-    }
-
-    @Test
-    public void testBind_chunkedTextWithNoImage() {
-        ImageBindingRef imageBinding =
-                ImageBindingRef.newBuilder().setBindingId("image-binding-id").build();
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setImageChunk(
-                                        StyledImageChunk.newBuilder().setImageBinding(
-                                                imageBinding))))
-                        .build();
-        when(mFrameContext.getImageBindingValue(imageBinding))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEmpty();
-        verify(mFrameContext)
-                .reportMessage(MessageType.ERROR, ERR_MISSING_BINDING_VALUE,
-                        "No image found for binding id: image-binding-id");
-    }
-
-    @Test
-    public void testBind_styledImageChunkWithNoContent() {
-        TextElement chunkedTextElement =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.newBuilder().setImageChunk(
-                                        StyledImageChunk.getDefaultInstance())))
-                        .build();
-        when(mMockTextEvaluator.evaluate(
-                     mMockAssetProvider, ParameterizedText.getDefaultInstance()))
-                .thenReturn("");
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEmpty();
-        ImageSpan[] imageSpans =
-                ((SpannedString) mAdapter.getBaseView().getText()).getSpans(0, 1, ImageSpan.class);
-        assertThat(imageSpans).hasLength(0);
-
-        verify(mFrameContext)
-                .reportMessage(MessageType.ERROR, ERR_MISSING_OR_UNHANDLED_CONTENT,
-                        "StyledImageChunk missing Image content; has CONTENT_NOT_SET");
-    }
-
-    @Test
-    public void testBind_wrongContent_fails() {
-        TextElement elementWithWrongContent =
-                TextElement.newBuilder()
-                        .setParameterizedText(ParameterizedText.getDefaultInstance())
-                        .build();
-
-        mAdapter.createAdapter(asElement(elementWithWrongContent), mFrameContext);
-
-        assertThatRunnable(
-                () -> mAdapter.bindModel(asElement(elementWithWrongContent), mFrameContext))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unhandled type of TextElement");
-    }
-
-    @Test
-    public void testBind_missingContent_fails() {
-        TextElement emptyElement = TextElement.getDefaultInstance();
-
-        assertThatRunnable(() -> mAdapter.bindModel(asElement(emptyElement), mFrameContext))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unhandled type of TextElement");
-    }
-
-    @Test
-    public void testBind_textChunk() {
-        TextElement chunkedTextElement =
-                TextElement.newBuilder().setChunkedText(CHUNKED_TEXT_TEXT).build();
-
-        mAdapter.createAdapter(asElement(chunkedTextElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedTextElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(PROCESSED_TEXT);
-    }
-
-    @Test
-    public void testBind_imageChunk() {
-        TextElement chunkedImageElement =
-                TextElement.newBuilder()
-                        .setChunkedText(ChunkedText.newBuilder().addChunks(IMAGE_CHUNK))
-                        .build();
-
-        mAdapter.createAdapter(asElement(chunkedImageElement), mFrameContext);
-        mAdapter.bindModel(asElement(chunkedImageElement), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getText().toString()).isEqualTo(" ");
-        assertThat(
-                ((SpannedString) mAdapter.getBaseView().getText()).getSpans(0, 1, ImageSpan.class))
-                .hasLength(1);
-        assertThat(((SpannedString) mAdapter.getBaseView().getText())
-                           .getSpans(0, 1, ImageSpan.class)[0]
-                           .getVerticalAlignment())
-                .isEqualTo(ImageSpan.ALIGN_BASELINE);
-    }
-
-    @Test
-    public void testBind_emptyChunk_fails() {
-        TextElement elementWithEmptyChunk =
-                TextElement.newBuilder()
-                        .setChunkedText(
-                                ChunkedText.newBuilder().addChunks(Chunk.getDefaultInstance()))
-                        .build();
-
-        mAdapter.createAdapter(asElement(elementWithEmptyChunk), mFrameContext);
-
-        assertThatRunnable(
-                () -> mAdapter.bindModel(asElement(elementWithEmptyChunk), mFrameContext))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unhandled type of ChunkedText Chunk");
-    }
-
-    @Test
-    public void testSetTextOnView_optionalAbsent() {
-        TextElement chunkedTextBindingElement =
-                TextElement.newBuilder().setChunkedTextBinding(CHUNKED_TEXT_BINDING_REF).build();
-        when(mFrameContext.getChunkedTextBindingValue(CHUNKED_TEXT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setChunkedText(CHUNKED_TEXT_TEXT).build());
-        mAdapter.createAdapter(asElement(chunkedTextBindingElement), mFrameContext);
-
-        ChunkedTextBindingRef optionalBindingRef =
-                CHUNKED_TEXT_BINDING_REF.toBuilder().setIsOptional(true).build();
-        TextElement chunkedTextBindingElementOptional =
-                TextElement.newBuilder().setChunkedTextBinding(optionalBindingRef).build();
-        when(mFrameContext.getChunkedTextBindingValue(optionalBindingRef))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.setTextOnView(mFrameContext, chunkedTextBindingElementOptional);
-        assertThat(mAdapter.getBaseView().getText().toString()).isEmpty();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void testSetTextOnView_noContent() {
-        TextElement chunkedTextBindingElement =
-                TextElement.newBuilder().setChunkedTextBinding(CHUNKED_TEXT_BINDING_REF).build();
-        mAdapter.createAdapter(
-                asElement(TextElement.newBuilder().setChunkedText(CHUNKED_TEXT_TEXT).build()),
-                mFrameContext);
-
-        when(mFrameContext.getChunkedTextBindingValue(CHUNKED_TEXT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(BINDING_ID)
-                                    .setVisibility(Visibility.INVISIBLE)
-                                    .build());
-
-        assertThatRunnable(() -> mAdapter.setTextOnView(mFrameContext, chunkedTextBindingElement))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Chunked text binding PB had no content");
-    }
-
-    @Test
-    public void testAddTextChunk_setsTextAfterEvaluatingParameterizedText() {
-        mAdapter.addTextChunk(mFrameContext, mSpannable, TEXT_CHUNK);
-
-        verify(mMockTextEvaluator)
-                .evaluate(mMockAssetProvider, TEXT_CHUNK.getTextChunk().getParameterizedText());
-
-        assertThat(mSpannable.toString()).isEqualTo(PROCESSED_TEXT);
-    }
-
-    @Test
-    public void testAddTextChunk_setsStyles() {
-        int color = 314159;
-        mContext.getResources().getDisplayMetrics().scaledDensity = 1.5f;
-        Font font = Font.newBuilder().setItalic(true).setSize(20).build();
-        // The text scales with accessibility settings: size (20) x scaledDensity (1.5) = final size
-        // 30
-        int expectedTextSize = 30;
-
-        when(mMockStyleProvider.hasColor()).thenReturn(true);
-        when(mMockStyleProvider.getColor()).thenReturn(color);
-        when(mMockStyleProvider.getFont()).thenReturn(font);
-
-        mAdapter.addTextChunk(mFrameContext, mSpannable, TEXT_CHUNK);
-
-        assertThat(mSpannable.getSpans(0, PROCESSED_TEXT.length(), Object.class)).hasLength(3);
-
-        ForegroundColorSpan[] colorSpans =
-                mSpannable.getSpans(0, PROCESSED_TEXT.length(), ForegroundColorSpan.class);
-        assertThat(colorSpans[0].getForegroundColor()).isEqualTo(color);
-
-        AbsoluteSizeSpan[] sizeSpans =
-                mSpannable.getSpans(0, PROCESSED_TEXT.length(), AbsoluteSizeSpan.class);
-        assertThat(sizeSpans[0].getSize()).isEqualTo(expectedTextSize);
-
-        StyleSpan[] styleSpans = mSpannable.getSpans(0, PROCESSED_TEXT.length(), StyleSpan.class);
-        assertThat(styleSpans[0].getStyle()).isEqualTo(Typeface.ITALIC);
-    }
-
-    @Test
-    public void testAddTextChunk_addsMargins() {
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(STYLE_WIDTH_PX);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(STYLE_HEIGHT_PX);
-
-        when(mMockStyleProvider.getMargins())
-                .thenReturn(EdgeWidths.newBuilder().setStart(11).setEnd(22).build());
-
-        // Required to set up the local frameContext member var.
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-
-        mAdapter.addTextChunk(mFrameContext, mSpannable, TEXT_CHUNK);
-
-        assertThat(mSpannable.toString()).isEqualTo(" smooth ");
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 8, ImageSpan.class);
-
-        Drawable leftMarginDrawable = imageSpans[0].getDrawable();
-        assertThat(leftMarginDrawable.getBounds().left).isEqualTo(0);
-        assertThat(leftMarginDrawable.getBounds().right).isEqualTo(11);
-
-        Drawable rightMarginDrawable = imageSpans[1].getDrawable();
-        assertThat(leftMarginDrawable.getBounds().left).isEqualTo(0);
-        assertThat(rightMarginDrawable.getBounds().right).isEqualTo(22);
-    }
-
-    @Test
-    public void testAddImageChunk_setsImageAndDims() {
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(STYLE_WIDTH_PX);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(STYLE_HEIGHT_PX);
-        when(mMockStyleProvider.hasPreLoadFill()).thenReturn(true);
-        Drawable preLoadFill = new ColorDrawable(Color.CYAN);
-        when(mMockStyleProvider.createPreLoadFill()).thenReturn(preLoadFill);
-
-        // Required to set up the local frameContext member var.
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-
-        mAdapter.addImageChunk(mFrameContext, mTextView, mSpannable, IMAGE_CHUNK);
-
-        assertThat(mSpannable.toString()).isEqualTo(" ");
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 1, ImageSpan.class);
-        LayerDrawable containerDrawable = (LayerDrawable) imageSpans[0].getDrawable();
-
-        ArgumentCaptor<ImageSpanDrawableCallback> imageCallbackCaptor =
-                ArgumentCaptor.forClass(ImageSpanDrawableCallback.class);
-        verify(mMockAssetProvider)
-                .getImage(eq(IMAGE_CHUNK_IMAGE), eq(STYLE_WIDTH_PX), eq(STYLE_HEIGHT_PX),
-                        imageCallbackCaptor.capture());
-
-        // Check for the pre-load fill
-        assertThat(containerDrawable.getDrawable(0)).isSameInstanceAs(preLoadFill);
-
-        // Activate the image loading callback
-        Drawable imageDrawable = new ColorDrawable(123);
-        imageCallbackCaptor.getValue().accept(imageDrawable);
-
-        // Assert that we set the image on the span
-        assertThat(containerDrawable.getDrawable(0)).isSameInstanceAs(imageDrawable);
-
-        assertThat(imageDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, STYLE_WIDTH_PX, STYLE_HEIGHT_PX));
-        assertThat(containerDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, STYLE_WIDTH_PX, STYLE_HEIGHT_PX));
-    }
-
-    @Test
-    public void testAddImageChunk_setsImageAndDimsWidthNotSpecified() {
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(-1);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(STYLE_HEIGHT_PX);
-        when(mMockStyleProvider.hasPreLoadFill()).thenReturn(true);
-        Drawable preLoadFill = new ColorDrawable(Color.CYAN);
-        when(mMockStyleProvider.createPreLoadFill()).thenReturn(preLoadFill);
-
-        // Required to set up the local mFrameContext member var.
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-
-        mAdapter.addImageChunk(mFrameContext, mTextView, mSpannable, IMAGE_CHUNK);
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 1, ImageSpan.class);
-        LayerDrawable containerDrawable = (LayerDrawable) imageSpans[0].getDrawable();
-
-        ArgumentCaptor<ImageSpanDrawableCallback> imageCallbackCaptor =
-                ArgumentCaptor.forClass(ImageSpanDrawableCallback.class);
-        verify(mMockAssetProvider)
-                .getImage(eq(IMAGE_CHUNK_IMAGE), eq(-1), eq(STYLE_HEIGHT_PX),
-                        imageCallbackCaptor.capture());
-
-        int aspectRatio = 4;
-        Drawable imageDrawable = new ColorDrawable(123) {
-            @Override
-            public int getIntrinsicWidth() {
-                return 11 * aspectRatio;
-            }
-
-            @Override
-            public int getIntrinsicHeight() {
-                return 11;
-            }
-        };
-
-        // Activate the image loading callback
-        imageCallbackCaptor.getValue().accept(imageDrawable);
-
-        // Assert that we set the image on the span
-        assertThat(containerDrawable.getDrawable(0)).isSameInstanceAs(imageDrawable);
-
-        assertThat(imageDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, STYLE_HEIGHT_PX * 4, STYLE_HEIGHT_PX));
-        assertThat(containerDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, STYLE_HEIGHT_PX * 4, STYLE_HEIGHT_PX));
-    }
-
-    @Test
-    public void testAddImageChunk_addsMargins() {
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(STYLE_WIDTH_PX);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(STYLE_HEIGHT_PX);
-
-        when(mMockStyleProvider.getMargins())
-                .thenReturn(EdgeWidths.newBuilder().setStart(11).setEnd(22).build());
-
-        // Required to set up the local frameContext member var.
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-
-        mAdapter.addImageChunk(mFrameContext, mTextView, mSpannable, IMAGE_CHUNK);
-
-        assertThat(mSpannable.toString()).isEqualTo("   ");
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 3, ImageSpan.class);
-
-        Drawable leftMarginDrawable = imageSpans[0].getDrawable();
-        assertThat(leftMarginDrawable.getBounds().left).isEqualTo(0);
-        assertThat(leftMarginDrawable.getBounds().right).isEqualTo(11);
-
-        Drawable rightMarginDrawable = imageSpans[2].getDrawable();
-        assertThat(leftMarginDrawable.getBounds().left).isEqualTo(0);
-        assertThat(rightMarginDrawable.getBounds().right).isEqualTo(22);
-    }
-
-    @Test
-    public void testAddImageChunk_tintColor() {
-        // Required to set up the local frameContext member var.
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-
-        StyleProvider tintStyleProvider = new StyleProvider(
-                Style.newBuilder().setStyleId("tint").setColor(0xFEEDFACE).build(),
-                mMockAssetProvider);
-        StyleIdsStack tintStyle = StyleIdsStack.newBuilder().addStyleIds("tint").build();
-        when(mFrameContext.makeStyleFor(tintStyle)).thenReturn(tintStyleProvider);
-
-        Chunk imageChunk = Chunk.newBuilder()
-                                   .setImageChunk(StyledImageChunk.newBuilder()
-                                                          .setImage(IMAGE_CHUNK_IMAGE)
-                                                          .setStyleReferences(tintStyle))
-                                   .build();
-
-        mAdapter.addImageChunk(mFrameContext, mTextView, mSpannable, imageChunk);
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 1, ImageSpan.class);
-        LayerDrawable containerDrawable = (LayerDrawable) imageSpans[0].getDrawable();
-
-        ArgumentCaptor<ImageSpanDrawableCallback> imageCallbackCaptor =
-                ArgumentCaptor.forClass(ImageSpanDrawableCallback.class);
-        verify(mMockAssetProvider)
-                .getImage(eq(IMAGE_CHUNK_IMAGE), anyInt(), anyInt(), imageCallbackCaptor.capture());
-
-        // Activate the image loading callback
-        imageCallbackCaptor.getValue().accept(mDrawable);
-
-        // Assert that we set the image on the span
-        assertThat(containerDrawable.getDrawable(0).getColorFilter())
-                .isEqualTo(new PorterDuffColorFilter(0xFEEDFACE, Mode.SRC_IN));
-    }
-
-    @Test
-    public void testBindSetsActions_inline() {
-        TextElement imageChunkWithActions =
-                TextElement.newBuilder()
-                        .setChunkedText(ChunkedText.newBuilder().addChunks(
-                                IMAGE_CHUNK.toBuilder().setActions(
-                                        Actions.newBuilder().setOnClickAction(
-                                                Action.getDefaultInstance()))))
-                        .build();
-        when(mFrameContext.getFrame()).thenReturn(Frame.getDefaultInstance());
-
-        mAdapter.createAdapter(asElement(imageChunkWithActions), mFrameContext);
-        mAdapter.bindModel(asElement(imageChunkWithActions), mFrameContext);
-
-        assertThat(((SpannedString) mAdapter.getBaseView().getText())
-                           .getSpans(0, 1, ActionsClickableSpan.class))
-                .hasLength(1);
-        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
-        mAdapter.getBaseView().dispatchTouchEvent(motionEvent);
-        verify(mMockActionHandler)
-                .handleAction(Action.getDefaultInstance(), ActionType.CLICK,
-                        Frame.getDefaultInstance(), mAdapter.getBaseView(),
-                        LogData.getDefaultInstance());
-    }
-
-    @Test
-    public void testBindSetsActions_bind() {
-        String bindingId = "ACTION";
-        ActionsBindingRef binding = ActionsBindingRef.newBuilder().setBindingId(bindingId).build();
-        TextElement imageChunkWithActions =
-                TextElement.newBuilder()
-                        .setChunkedText(ChunkedText.newBuilder().addChunks(
-                                IMAGE_CHUNK.toBuilder().setActionsBinding(binding)))
-                        .build();
-        when(mFrameContext.getActionsFromBinding(binding))
-                .thenReturn(
-                        Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build());
-        when(mFrameContext.getFrame()).thenReturn(Frame.getDefaultInstance());
-
-        mAdapter.createAdapter(asElement(imageChunkWithActions), mFrameContext);
-        mAdapter.bindModel(asElement(imageChunkWithActions), mFrameContext);
-
-        assertThat(((SpannedString) mAdapter.getBaseView().getText())
-                           .getSpans(0, 1, ActionsClickableSpan.class))
-                .hasLength(1);
-        MotionEvent motionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
-        mAdapter.getBaseView().dispatchTouchEvent(motionEvent);
-        verify(mMockActionHandler)
-                .handleAction(Action.getDefaultInstance(), ActionType.CLICK,
-                        Frame.getDefaultInstance(), mAdapter.getBaseView(),
-                        LogData.getDefaultInstance());
-    }
-
-    @Test
-    public void testBindSetsActions_bindingNotFound() {
-        String bindingId = "ACTION";
-        ActionsBindingRef binding = ActionsBindingRef.newBuilder().setBindingId(bindingId).build();
-        TextElement imageChunkWithActions =
-                TextElement.newBuilder()
-                        .setChunkedText(ChunkedText.newBuilder().addChunks(
-                                IMAGE_CHUNK.toBuilder().setActionsBinding(binding)))
-                        .build();
-        when(mFrameContext.getActionsFromBinding(binding)).thenReturn(Actions.getDefaultInstance());
-        when(mFrameContext.getFrame()).thenReturn(Frame.getDefaultInstance());
-
-        mAdapter.createAdapter(asElement(imageChunkWithActions), mFrameContext);
-        mAdapter.bindModel(asElement(imageChunkWithActions), mFrameContext);
-
-        // Completes successfully, but doesn't add the clickable span.
-        assertThat(((SpannedString) mAdapter.getBaseView().getText())
-                           .getSpans(0, 1, ActionsClickableSpan.class))
-                .isEmpty();
-    }
-
-    @Test
-    public void testUnbind_cancelsCallbacks() {
-        mAdapter.createAdapter(asElement(TextElement.getDefaultInstance()), mFrameContext);
-        mAdapter.addImageChunk(mFrameContext, mTextView, mSpannable, IMAGE_CHUNK);
-
-        ImageSpan[] imageSpans = mSpannable.getSpans(0, 1, ImageSpan.class);
-        LayerDrawable containerDrawable = (LayerDrawable) imageSpans[0].getDrawable();
-        ArgumentCaptor<ImageSpanDrawableCallback> imageCallbackCaptor =
-                ArgumentCaptor.forClass(ImageSpanDrawableCallback.class);
-        verify(mMockAssetProvider)
-                .getImage(eq(IMAGE_CHUNK_IMAGE), eq(DIMENSION_UNKNOWN), eq(DIMENSION_UNKNOWN),
-                        imageCallbackCaptor.capture());
-
-        // Unbind the model
-        mAdapter.unbindModel();
-
-        // Activate the image loading callback
-        Drawable imageDrawable = new ColorDrawable(Color.RED);
-        imageCallbackCaptor.getValue().accept(imageDrawable);
-
-        // Assert that we did NOT set the image on the span
-        assertThat(containerDrawable.getDrawable(SINGLE_LAYER_ID))
-                .isNotSameInstanceAs(imageDrawable);
-    }
-
-    @Test
-    public void testKeySupplier() {
-        ChunkedTextElementAdapter.KeySupplier keySupplier =
-                new ChunkedTextElementAdapter.KeySupplier();
-        assertThat(keySupplier.getAdapterTag()).isEqualTo("ChunkedTextElementAdapter");
-        assertThat(keySupplier.getAdapter(mContext, mAdapterParameters)).isNotNull();
-        assertThat(keySupplier).isNotInstanceOf(SingletonKeySupplier.class);
-    }
-
-    @Test
-    public void testSetBounds_heightAndWidth_setsBoth() {
-        int widthDp = 123;
-        int heightDp = 456;
-        mAdapter.setBounds(mDrawable,
-                new StyleProvider(Style.newBuilder().setHeight(heightDp).setWidth(widthDp).build(),
-                        mMockAssetProvider),
-                mTextView);
-
-        int widthPx = (int) LayoutUtils.dpToPx(widthDp, mContext);
-        int heightPx = (int) LayoutUtils.dpToPx(heightDp, mContext);
-        assertThat(mDrawable.getBounds()).isEqualTo(new Rect(0, 0, widthPx, heightPx));
-    }
-
-    @Test
-    public void testSetBounds_heightOnly_aspectRatioScaled() {
-        mAdapter.setBounds(mDrawable,
-                new StyleProvider(
-                        Style.newBuilder().setHeight(HEIGHT_DP).build(), mMockAssetProvider),
-                mTextView);
-
-        int heightPx = (int) LayoutUtils.dpToPx(HEIGHT_DP, mContext);
-        assertThat(mDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, heightPx * IMAGE_ASPECT_RATIO, heightPx));
-    }
-
-    @Test
-    public void testSetBounds_widthOnly_aspectRatioScaled() {
-        mAdapter.setBounds(mDrawable,
-                new StyleProvider(
-                        Style.newBuilder().setWidth(WIDTH_DP).build(), mMockAssetProvider),
-                mTextView);
-
-        int widthPx = (int) LayoutUtils.dpToPx(WIDTH_DP, mContext);
-        assertThat(mDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, widthPx, widthPx / IMAGE_ASPECT_RATIO));
-    }
-
-    @Test
-    public void testSetBounds_noHeightOrWidth_defaultsToTextHeight() {
-        mAdapter.setBounds(mDrawable,
-                new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider), mTextView);
-
-        assertThat(mDrawable.getBounds())
-                .isEqualTo(new Rect(0, 0, TEXT_HEIGHT * IMAGE_ASPECT_RATIO, TEXT_HEIGHT));
-    }
-
-    @Implements(TextView.class)
-    public static class ShadowTextViewWithHeight extends ShadowTextView {
-        @Implementation
-        public int getLineHeight() {
-            return TEXT_HEIGHT;
-        }
-    }
-
-    private static Element asElement(TextElement textElement) {
-        return Element.newBuilder().setTextElement(textElement).build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/CustomElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/CustomElementAdapterTest.java
deleted file mode 100644
index 50cda0b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/CustomElementAdapterTest.java
+++ /dev/null
@@ -1,238 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.CustomElementAdapter.KeySupplier;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.CustomBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElementData;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link CustomElementAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CustomElementAdapterTest {
-    private static final CustomElementData DUMMY_DATA = CustomElementData.getDefaultInstance();
-    private static final Element DEFAULT_MODEL =
-            asElement(CustomElement.newBuilder().setCustomElementData(DUMMY_DATA).build());
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private CustomElementProvider mCustomElementProvider;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private Context mContext;
-    private View mCustomView;
-    private CustomElementAdapter mAdapter;
-    private AdapterParameters mAdapterParameters;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mCustomView = new View(mContext);
-
-        when(mHostProviders.getCustomElementProvider()).thenReturn(mCustomElementProvider);
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mAssetProvider.isRtL()).thenReturn(false);
-        when(mCustomElementProvider.createCustomElement(DUMMY_DATA)).thenReturn(mCustomView);
-        when(mFrameContext.reportMessage(anyInt(), any(), anyString()))
-                .thenAnswer(invocation -> invocation.getArguments()[2]);
-
-        mAdapterParameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mHostProviders, new FakeClock(), false, false);
-
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class)))
-                .thenReturn(mAdapterParameters.mDefaultStyleProvider);
-
-        mAdapter = new KeySupplier().getAdapter(mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testCreate() {
-        assertThat(mAdapter).isNotNull();
-    }
-
-    @Test
-    public void testCreateAdapter_initializes() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getView()).isNotNull();
-        assertThat(mAdapter.getKey()).isNotNull();
-    }
-
-    @Test
-    public void testCreateAdapter_ignoresSubsequentCalls() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        View adapterView = mAdapter.getView();
-        RecyclerKey adapterKey = mAdapter.getKey();
-
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getView()).isSameInstanceAs(adapterView);
-        assertThat(mAdapter.getKey()).isSameInstanceAs(adapterKey);
-    }
-
-    @Test
-    public void testBindModel_data() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0)).isEqualTo(mCustomView);
-    }
-
-    @Test
-    public void testBindModel_binding() {
-        CustomBindingRef bindingRef = CustomBindingRef.newBuilder().setBindingId("CUSTOM!").build();
-        when(mFrameContext.getCustomElementBindingValue(bindingRef))
-                .thenReturn(BindingValue.newBuilder().setCustomElementData(DUMMY_DATA).build());
-
-        Element model = asElement(CustomElement.newBuilder().setCustomBinding(bindingRef).build());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0)).isEqualTo(mCustomView);
-    }
-
-    @Test
-    public void testBindModel_noContent() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(asElement(CustomElement.getDefaultInstance()), mFrameContext);
-
-        verifyZeroInteractions(mCustomElementProvider);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testBindModel_optionalAbsent() {
-        CustomBindingRef bindingRef =
-                CustomBindingRef.newBuilder().setBindingId("CUSTOM!").setIsOptional(true).build();
-        when(mFrameContext.getCustomElementBindingValue(bindingRef))
-                .thenReturn(BindingValue.getDefaultInstance());
-        Element model = asElement(CustomElement.newBuilder().setCustomBinding(bindingRef).build());
-        mAdapter.createAdapter(model, mFrameContext);
-
-        // This should not fail.
-        mAdapter.bindModel(model, mFrameContext);
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void testBindModel_noContentInBinding() {
-        CustomBindingRef bindingRef = CustomBindingRef.newBuilder().setBindingId("CUSTOM").build();
-        when(mFrameContext.getCustomElementBindingValue(bindingRef))
-                .thenReturn(BindingValue.newBuilder().setBindingId("CUSTOM").build());
-        Element model = asElement(CustomElement.newBuilder().setCustomBinding(bindingRef).build());
-        mAdapter.createAdapter(model, mFrameContext);
-
-        assertThatRunnable(() -> mAdapter.bindModel(model, mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Custom element binding CUSTOM had no content");
-    }
-
-    @Test
-    public void testUnbindModel() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        verify(mCustomElementProvider).releaseCustomView(mCustomView, DUMMY_DATA);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testUnbindModel_noChildren() {
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        verifyZeroInteractions(mCustomElementProvider);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testUnbindModel_multipleChildren() {
-        View customView2 = new View(mContext);
-        when(mCustomElementProvider.createCustomElement(DUMMY_DATA))
-                .thenReturn(mCustomView)
-                .thenReturn(customView2);
-
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        verify(mCustomElementProvider).releaseCustomView(mCustomView, DUMMY_DATA);
-        verify(mCustomElementProvider).releaseCustomView(customView2, DUMMY_DATA);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        CustomElement model = CustomElement.newBuilder().build();
-
-        Element elementWithModel = Element.newBuilder().setCustomElement(model).build();
-        assertThat(mAdapter.getModelFromElement(elementWithModel)).isSameInstanceAs(model);
-
-        Element elementWithWrongModel =
-                Element.newBuilder().setTextElement(TextElement.getDefaultInstance()).build();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(elementWithWrongModel))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing CustomElement");
-
-        Element emptyElement = Element.getDefaultInstance();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(emptyElement))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing CustomElement");
-    }
-
-    private static Element asElement(CustomElement customElement) {
-        return Element.newBuilder().setCustomElement(customElement).build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/DebugLoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/DebugLoggerTest.java
deleted file mode 100644
index 8af68ec..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/DebugLoggerTest.java
+++ /dev/null
@@ -1,155 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.ErrorCodeAndMessage;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link DebugLogger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class DebugLoggerTest {
-    private static final String ERROR_TEXT_1 = "Interdimensional rift formation.";
-    private static final String ERROR_TEXT_2 = "Exotic particle containment breach.";
-    private static final String WARNING_TEXT = "Noncompliant meson entanglement.";
-
-    private Context mContext;
-
-    private DebugLogger mDebugLogger;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mDebugLogger = new DebugLogger();
-    }
-
-    @Test
-    public void testGetReportView_singleError() {
-        mDebugLogger.recordMessage(MessageType.ERROR, ERROR_TEXT_1);
-
-        LinearLayout reportView =
-                (LinearLayout) mDebugLogger.getReportView(MessageType.ERROR, mContext);
-
-        assertThat(reportView.getOrientation()).isEqualTo(LinearLayout.VERTICAL);
-        assertThat(reportView.getLayoutParams().width).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(reportView.getLayoutParams().height).isEqualTo(LayoutParams.WRAP_CONTENT);
-
-        assertThat(reportView.getChildCount()).isEqualTo(2);
-
-        // Check the divider
-        assertThat(reportView.getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(reportView.getChildAt(0).getLayoutParams().height)
-                .isEqualTo((int) LayoutUtils.dpToPx(DebugLogger.ERROR_DIVIDER_WIDTH_DP, mContext));
-
-        // Check the error box
-        assertThat(reportView.getChildAt(1)).isInstanceOf(TextView.class);
-        TextView textErrorView = (TextView) reportView.getChildAt(1);
-        assertThat(textErrorView.getText().toString()).isEqualTo(ERROR_TEXT_1);
-
-        // Check that padding has been set (but don't check specific values)
-        assertThat(textErrorView.getPaddingBottom()).isNotEqualTo(0);
-        assertThat(textErrorView.getPaddingTop()).isNotEqualTo(0);
-        assertThat(textErrorView.getPaddingStart()).isNotEqualTo(0);
-        assertThat(textErrorView.getPaddingEnd()).isNotEqualTo(0);
-    }
-
-    @Test
-    public void testGetReportView_multipleErrors() {
-        mDebugLogger.recordMessage(MessageType.ERROR, ERROR_TEXT_1);
-        mDebugLogger.recordMessage(MessageType.ERROR, ERROR_TEXT_2);
-
-        LinearLayout reportView =
-                (LinearLayout) mDebugLogger.getReportView(MessageType.ERROR, mContext);
-
-        assertThat(reportView.getChildCount()).isEqualTo(3);
-
-        assertThat(((TextView) reportView.getChildAt(1)).getText().toString())
-                .isEqualTo(ERROR_TEXT_1);
-        assertThat(((TextView) reportView.getChildAt(2)).getText().toString())
-                .isEqualTo(ERROR_TEXT_2);
-    }
-
-    @Test
-    public void testGetReportView_zeroErrors() {
-        LinearLayout errorView =
-                (LinearLayout) mDebugLogger.getReportView(MessageType.ERROR, mContext);
-
-        assertThat(errorView).isNull();
-    }
-
-    @Test
-    public void testGetMessageTypes() {
-        assertThat(mDebugLogger.getMessages(MessageType.ERROR)).isEmpty();
-        assertThat(mDebugLogger.getMessages(MessageType.WARNING)).isEmpty();
-
-        mDebugLogger.recordMessage(MessageType.WARNING, WARNING_TEXT);
-        assertThat(mDebugLogger.getMessages(MessageType.ERROR)).isEmpty();
-        assertThat(mDebugLogger.getMessages(MessageType.WARNING))
-                .containsExactly(new ErrorCodeAndMessage(ErrorCode.ERR_UNSPECIFIED, WARNING_TEXT));
-
-        mDebugLogger.recordMessage(MessageType.ERROR, ErrorCode.ERR_PROTO_TOO_LARGE, ERROR_TEXT_1);
-        assertThat(mDebugLogger.getMessages(MessageType.ERROR))
-                .containsExactly(
-                        new ErrorCodeAndMessage(ErrorCode.ERR_PROTO_TOO_LARGE, ERROR_TEXT_1));
-        assertThat(mDebugLogger.getMessages(MessageType.WARNING))
-                .containsExactly(new ErrorCodeAndMessage(ErrorCode.ERR_UNSPECIFIED, WARNING_TEXT));
-    }
-
-    @Test
-    public void testGetReportView_byType() {
-        mDebugLogger.recordMessage(MessageType.ERROR, ERROR_TEXT_1);
-        mDebugLogger.recordMessage(MessageType.WARNING, WARNING_TEXT);
-
-        LinearLayout errorView =
-                (LinearLayout) mDebugLogger.getReportView(MessageType.ERROR, mContext);
-
-        assertThat(errorView.getChildCount()).isEqualTo(2);
-        assertThat(((TextView) errorView.getChildAt(1)).getText().toString())
-                .isEqualTo(ERROR_TEXT_1);
-
-        LinearLayout warningView =
-                (LinearLayout) mDebugLogger.getReportView(MessageType.WARNING, mContext);
-
-        assertThat(warningView.getChildCount()).isEqualTo(2);
-        assertThat(((TextView) warningView.getChildAt(1)).getText().toString())
-                .isEqualTo(WARNING_TEXT);
-    }
-
-    @Test
-    public void testGetErrorCodes() {
-        mDebugLogger.recordMessage(MessageType.ERROR, ErrorCode.ERR_MISSING_FONTS, ERROR_TEXT_1);
-        mDebugLogger.recordMessage(MessageType.ERROR, ErrorCode.ERR_MISSING_FONTS, ERROR_TEXT_2);
-        mDebugLogger.recordMessage(MessageType.ERROR, ErrorCode.ERR_DUPLICATE_STYLE, ERROR_TEXT_1);
-        mDebugLogger.recordMessage(MessageType.WARNING, ErrorCode.ERR_MISSING_FONTS, WARNING_TEXT);
-        mDebugLogger.recordMessage(
-                MessageType.WARNING, ErrorCode.ERR_DUPLICATE_TEMPLATE, WARNING_TEXT);
-
-        assertThat(mDebugLogger.getErrorCodes())
-                .containsExactly(ErrorCode.ERR_MISSING_FONTS, ErrorCode.ERR_MISSING_FONTS,
-                        ErrorCode.ERR_DUPLICATE_STYLE, ErrorCode.ERR_MISSING_FONTS,
-                        ErrorCode.ERR_DUPLICATE_TEMPLATE);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterFactoryTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterFactoryTest.java
deleted file mode 100644
index a57ebcd3..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterFactoryTest.java
+++ /dev/null
@@ -1,334 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.TemplateBinder.TemplateKey;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ChunkedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementStack;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ChunkedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for the ElementAdapterFactory. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ElementAdapterFactoryTest {
-    @Mock
-    private FrameContext mFrameContext;
-
-    @Mock
-    private CustomElementAdapter mCustomElementAdapter;
-    @Mock
-    private ChunkedTextElementAdapter mChunkedTextElementAdapter;
-    @Mock
-    private ParameterizedTextElementAdapter mParameterizedTextElementAdapter;
-    @Mock
-    private ImageElementAdapter mImageElementAdapter;
-    @Mock
-    private GridRowAdapter mGridRowAdapter;
-    @Mock
-    private ElementListAdapter mElementListAdapter;
-    @Mock
-    private ElementAdapter<? extends View, ?> mTemplateAdapter;
-    @Mock
-    private ElementStackAdapter mElementStackAdapter;
-
-    @Mock
-    private AdapterFactory<CustomElementAdapter, CustomElement> mCustomElementFactory;
-
-    @Mock
-    private AdapterFactory<ChunkedTextElementAdapter, Element> mChunkedTextElementFactory;
-
-    @Mock
-    private AdapterFactory<ParameterizedTextElementAdapter, Element>
-            mParameterizedTextElementFactory;
-
-    @Mock
-    private AdapterFactory<ImageElementAdapter, ImageElement> mImageElementFactory;
-
-    @Mock
-    private AdapterFactory<GridRowAdapter, GridRow> mGridRowFactory;
-
-    @Mock
-    private AdapterFactory<ElementListAdapter, ElementList> mElementListFactory;
-
-    @Mock
-    private AdapterFactory<ElementStackAdapter, ElementStack> mElementStackFactory;
-
-    @Mock
-    private KeyedRecyclerPool<ElementAdapter<? extends View, ?>> mTemplateRecyclerPool;
-
-    private final List<AdapterFactory<?, ?>> mAdapterFactories = new ArrayList<>();
-
-    private ElementAdapterFactory mAdapterFactory;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        when(mFrameContext.reportMessage(anyInt(), any(), anyString()))
-                .thenAnswer(invocation -> invocation.getArguments()[2]);
-        mAdapterFactory = new ElementAdapterFactory(mCustomElementFactory,
-                mChunkedTextElementFactory, mParameterizedTextElementFactory, mImageElementFactory,
-                mGridRowFactory, mElementListFactory, mElementStackFactory, mTemplateRecyclerPool);
-        when(mCustomElementFactory.get(any(CustomElement.class), eq(mFrameContext)))
-                .thenReturn(mCustomElementAdapter);
-        when(mChunkedTextElementFactory.get(any(Element.class), eq(mFrameContext)))
-                .thenReturn(mChunkedTextElementAdapter);
-        when(mParameterizedTextElementFactory.get(any(Element.class), eq(mFrameContext)))
-                .thenReturn(mParameterizedTextElementAdapter);
-        when(mImageElementFactory.get(any(ImageElement.class), eq(mFrameContext)))
-                .thenReturn(mImageElementAdapter);
-        when(mGridRowFactory.get(any(GridRow.class), eq(mFrameContext)))
-                .thenReturn(mGridRowAdapter);
-        when(mElementListFactory.get(any(ElementList.class), eq(mFrameContext)))
-                .thenReturn(mElementListAdapter);
-        when(mElementStackFactory.get(any(ElementStack.class), eq(mFrameContext)))
-                .thenReturn(mElementStackAdapter);
-        mAdapterFactories.add(mCustomElementFactory);
-        mAdapterFactories.add(mChunkedTextElementFactory);
-        mAdapterFactories.add(mParameterizedTextElementFactory);
-        mAdapterFactories.add(mImageElementFactory);
-        mAdapterFactories.add(mGridRowFactory);
-        mAdapterFactories.add(mElementListFactory);
-        mAdapterFactories.add(mElementStackFactory);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_customElement() {
-        CustomElement model = CustomElement.getDefaultInstance();
-        Element element = Element.newBuilder().setCustomElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mCustomElementAdapter);
-        verify(mCustomElementFactory).get(model, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_chunkedTextElement() {
-        TextElement model =
-                TextElement.newBuilder().setChunkedText(ChunkedText.getDefaultInstance()).build();
-        Element element = Element.newBuilder().setTextElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mChunkedTextElementAdapter);
-        verify(mChunkedTextElementFactory).get(element, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_chunkedTextBindingElement() {
-        TextElement model =
-                TextElement.newBuilder()
-                        .setChunkedTextBinding(ChunkedTextBindingRef.getDefaultInstance())
-                        .build();
-        Element element = Element.newBuilder().setTextElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mChunkedTextElementAdapter);
-        verify(mChunkedTextElementFactory).get(element, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_parameterizedTextElement() {
-        TextElement model = TextElement.newBuilder()
-                                    .setParameterizedText(ParameterizedText.getDefaultInstance())
-                                    .build();
-        Element element = Element.newBuilder().setTextElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mParameterizedTextElementAdapter);
-        verify(mParameterizedTextElementFactory).get(element, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_parameterizedTextBindingElement() {
-        TextElement model = TextElement.newBuilder()
-                                    .setParameterizedTextBinding(
-                                            ParameterizedTextBindingRef.getDefaultInstance())
-                                    .build();
-        Element element = Element.newBuilder().setTextElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mParameterizedTextElementAdapter);
-        verify(mParameterizedTextElementFactory).get(element, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_invalidTextElement() {
-        TextElement model = TextElement.getDefaultInstance();
-        Element element = Element.newBuilder().setTextElement(model).build();
-
-        assertThatRunnable(() -> mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unsupported TextElement type");
-    }
-
-    @Test
-    public void testCreateAdapterForElement_imageElement() {
-        ImageElement model = ImageElement.getDefaultInstance();
-        Element element = Element.newBuilder().setImageElement(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mImageElementAdapter);
-        verify(mImageElementFactory).get(model, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_gridRow() {
-        GridRow model = GridRow.getDefaultInstance();
-        Element element = Element.newBuilder().setGridRow(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mGridRowAdapter);
-        verify(mGridRowFactory).get(model, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_elementList() {
-        ElementList model = ElementList.getDefaultInstance();
-        Element element = Element.newBuilder().setElementList(model).build();
-        assertThat(mAdapterFactory.createAdapterForElement(element, mFrameContext))
-                .isSameInstanceAs(mElementListAdapter);
-        verify(mElementListFactory).get(model, mFrameContext);
-    }
-
-    @Test
-    public void testCreateAdapterForElement_unsupported() {
-        assertThatRunnable(()
-                                   -> mAdapterFactory.createAdapterForElement(
-                                           Element.getDefaultInstance(), mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unsupported Element type");
-    }
-
-    @Test
-    public void testReleaseAdapter_template() {
-        TemplateKey templateKey = new TemplateKey(Template.getDefaultInstance(), null, null);
-        when(mTemplateAdapter.getKey()).thenReturn(templateKey);
-
-        mAdapterFactory.releaseAdapter(mTemplateAdapter);
-
-        // We unbind the adapter but do not release it so it can be created quickly again.
-        verify(mTemplateAdapter).unbindModel();
-        verify(mTemplateAdapter, never()).releaseAdapter();
-        verify(mTemplateRecyclerPool).put(templateKey, mTemplateAdapter);
-    }
-
-    @Test
-    public void testReleaseAdapter_custom() {
-        mAdapterFactory.releaseAdapter(mCustomElementAdapter);
-        verify(mCustomElementFactory).release(mCustomElementAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_chunkedText() {
-        mAdapterFactory.releaseAdapter(mChunkedTextElementAdapter);
-        verify(mChunkedTextElementFactory).release(mChunkedTextElementAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_parameterizedText() {
-        mAdapterFactory.releaseAdapter(mParameterizedTextElementAdapter);
-        verify(mParameterizedTextElementFactory).release(mParameterizedTextElementAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_imageElement() {
-        mAdapterFactory.releaseAdapter(mImageElementAdapter);
-        verify(mImageElementFactory).release(mImageElementAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_gridRow() {
-        mAdapterFactory.releaseAdapter(mGridRowAdapter);
-        verify(mGridRowFactory).release(mGridRowAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_list() {
-        mAdapterFactory.releaseAdapter(mElementListAdapter);
-        verify(mElementListFactory).release(mElementListAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testReleaseAdapter_stacked() {
-        mAdapterFactory.releaseAdapter(mElementStackAdapter);
-        verify(mElementStackFactory).release(mElementStackAdapter);
-        testNoOtherFactoryInteractions();
-    }
-
-    @Test
-    public void testPurgeRecyclerPools() {
-        mAdapterFactory.purgeRecyclerPools();
-        for (AdapterFactory<?, ?> adapterFactory : mAdapterFactories) {
-            verify(adapterFactory).purgeRecyclerPool();
-        }
-    }
-
-    @Test
-    public void testPurgeTemplateRecyclerPool() {
-        Template template = Template.newBuilder()
-                                    .setTemplateId("template")
-                                    .setElement(Element.newBuilder().setElementList(
-                                            ElementList.getDefaultInstance()))
-                                    .build();
-        List<PietSharedState> sharedStates = Collections.emptyList();
-        when(mTemplateAdapter.getKey()).thenReturn(new TemplateKey(template, sharedStates, null));
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-
-        // Release adapter to populate the recycler pool
-        mAdapterFactory.releaseAdapter(mTemplateAdapter);
-
-        verify(mTemplateRecyclerPool).put(mTemplateAdapter.getKey(), mTemplateAdapter);
-
-        // Purge the recycler pools.
-        mAdapterFactory.purgeRecyclerPools();
-
-        verify(mTemplateRecyclerPool).clear();
-    }
-
-    private void testNoOtherFactoryInteractions() {
-        for (AdapterFactory<?, ?> adapterFactory : mAdapterFactories) {
-            verifyNoMoreInteractions(adapterFactory);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterTest.java
deleted file mode 100644
index b63a85e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterTest.java
+++ /dev/null
@@ -1,1160 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.piet.StyleProvider.DIMENSION_NOT_SET;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.piet.host.LogDataCallback;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerWrapperView;
-import org.chromium.chrome.browser.feed.library.testing.shadows.ExtendedShadowView;
-import org.chromium.components.feed.core.proto.ui.piet.AccessibilityProto.Accessibility;
-import org.chromium.components.feed.core.proto.ui.piet.AccessibilityProto.AccessibilityRole;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Actions;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.VisibilityAction;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ActionsBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.LogDataBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.VisibilityBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.VisibilityState;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ElementAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ElementAdapterTest {
-    private static final int WIDTH = 123;
-    private static final int HEIGHT = 456;
-    private static final boolean LEGACY_CORNERS_FLAG = false;
-    private static final boolean OUTLINE_CORNERS_FLAG = false;
-
-    private final LogData mLogDataTest = LogData.newBuilder().build();
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private StyleProvider mStyleProvider;
-
-    private Context mContext;
-    private AdapterParameters mParameters;
-    private View mView;
-    private RoundedCornerMaskCache mMaskCache;
-
-    private TestElementAdapter mAdapter;
-    private boolean mCallbackBound;
-    private boolean mCallbackUnbound;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        LogDataCallback logDataCallback = new LogDataCallback() {
-            @Override
-            public void onBind(LogData logData, View view) {
-                assertThat(mLogDataTest).isEqualTo(logData);
-                mCallbackBound = true;
-            }
-
-            @Override
-            public void onUnbind(LogData logData, View view) {
-                assertThat(mLogDataTest).isEqualTo(logData);
-                mCallbackUnbound = true;
-            }
-        };
-        mParameters = new AdapterParameters(mContext, Suppliers.of((ViewGroup) null),
-                mHostProviders, new FakeClock(), LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG);
-        mMaskCache = mParameters.mRoundedCornerMaskCache;
-        when(mHostProviders.getLogDataCallback()).thenReturn(logDataCallback);
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(mStyleProvider);
-        when(mFrameContext.getActionHandler()).thenReturn(mActionHandler);
-        when(mStyleProvider.hasRoundedCorners()).thenReturn(false);
-        when(mStyleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
-        when(mStyleProvider.createWrapperView(
-                     mContext, mMaskCache, LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG))
-                .thenReturn(new FrameLayout(mContext));
-
-        mView = new View(mContext);
-        mAdapter = new TestElementAdapter(mContext, mParameters, mView);
-    }
-
-    @Test
-    public void testGetters() {
-        // Pre-creation
-        assertThat(mAdapter.getHorizontalGravity(Gravity.CLIP_HORIZONTAL))
-                .isEqualTo(Gravity.CLIP_HORIZONTAL);
-        assertThat(mAdapter.getVerticalGravity(Gravity.CLIP_VERTICAL))
-                .isEqualTo(Gravity.CLIP_VERTICAL);
-        assertThat(mAdapter.getGravity(Gravity.AXIS_Y_SHIFT)).isEqualTo(Gravity.AXIS_Y_SHIFT);
-
-        Element defaultElement = Element.newBuilder().build();
-        Frame frame = Frame.newBuilder().setTag("FRAME").build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        when(mStyleProvider.getGravityHorizontal(Gravity.CLIP_HORIZONTAL))
-                .thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mStyleProvider.hasGravityHorizontal()).thenReturn(true);
-        when(mStyleProvider.hasGravityVertical()).thenReturn(true);
-        when(mStyleProvider.getGravityHorizontal(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mStyleProvider.getGravityVertical(anyInt())).thenReturn(Gravity.CENTER_VERTICAL);
-        // This should typically be CENTER_HORIZONTAL | CENTER_VERTICAL but I want to make sure that
-        // ElementAdapter is calling this method instead of OR'ing the H/V gravities itself.
-        when(mStyleProvider.getGravity(anyInt())).thenReturn(Gravity.FILL);
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-
-        assertThat(mAdapter.getContext()).isSameInstanceAs(mContext);
-        assertThat(mAdapter.getModel()).isSameInstanceAs(defaultElement);
-        assertThat(mAdapter.getRawModel()).isSameInstanceAs(defaultElement);
-        assertThat(mAdapter.getHorizontalGravity(Gravity.CLIP_HORIZONTAL))
-                .isEqualTo(Gravity.CENTER_HORIZONTAL);
-        assertThat(mAdapter.getParameters()).isSameInstanceAs(mParameters);
-        assertThat(mAdapter.getTemplatedStringEvaluator())
-                .isSameInstanceAs(mParameters.mTemplatedStringEvaluator);
-        assertThat(mAdapter.getElementStyle()).isSameInstanceAs(mStyleProvider);
-        assertThat(mAdapter.getElementStyleIdsStack())
-                .isEqualTo(StyleIdsStack.getDefaultInstance());
-
-        assertThat(mAdapter.getHorizontalGravity(Gravity.CLIP_VERTICAL))
-                .isEqualTo(Gravity.CENTER_HORIZONTAL);
-        assertThat(mAdapter.getVerticalGravity(Gravity.CLIP_HORIZONTAL))
-                .isEqualTo(Gravity.CENTER_VERTICAL);
-        assertThat(mAdapter.getGravity(Gravity.CLIP_VERTICAL)).isEqualTo(Gravity.FILL);
-    }
-
-    @Test
-    public void setLayoutParams() {
-        mAdapter.setLayoutParams(new LayoutParams(WIDTH, HEIGHT));
-
-        assertThat(mView.getLayoutParams().width).isEqualTo(WIDTH);
-        assertThat(mView.getLayoutParams().height).isEqualTo(HEIGHT);
-    }
-
-    @Test
-    public void testGetComputedDimensions_defaults() {
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void getComputedDimensions_explicit() {
-        mAdapter.setDims(WIDTH, HEIGHT);
-
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(WIDTH);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(HEIGHT);
-    }
-
-    @Test
-    public void createAdapter_callsOnCreateAdapter() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-    }
-
-    @Test
-    public void createAdapter_extractsModelFromElement() {
-        Element element = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(element, mFrameContext);
-
-        assertThat(mAdapter.getModel()).isEqualTo(TestElementAdapter.DEFAULT_MODEL);
-    }
-
-    @Test
-    public void testCreateAdapter_addsBorders() {
-        when(mStyleProvider.hasBorders()).thenReturn(true);
-
-        mAdapter.createAdapter(
-                Element.getDefaultInstance(), Element.getDefaultInstance(), mFrameContext);
-
-        verify(mStyleProvider)
-                .addBordersWithoutRoundedCorners((FrameLayout) mAdapter.getView(), mContext);
-    }
-
-    @Test
-    public void createAdapter_appliesVisibility() {
-        Element defaultElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.INVISIBLE))
-                        .build();
-
-        mAdapter.createAdapter(defaultElement, mFrameContext);
-
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.INVISIBLE);
-    }
-
-    @Test
-    public void createAdapter_doesNothingWithVisibilityGone() {
-        Element defaultElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-
-        mAdapter.createAdapter(defaultElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void createAdapter_ignoresBoundVisibility() {
-        String visibilityBindingId = "invisible";
-        VisibilityBindingRef visibilityBinding =
-                VisibilityBindingRef.newBuilder().setBindingId(visibilityBindingId).build();
-        Element defaultElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder()
-                                        .setDefaultVisibility(Visibility.VISIBLE)
-                                        .setOverridingBoundVisibility(visibilityBinding))
-                        .build();
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding)).thenReturn(Visibility.GONE);
-
-        mAdapter.createAdapter(defaultElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void createAdapter_setsDimensions() {
-        int width = 10;
-        int height = 20;
-
-        when(mStyleProvider.hasWidth()).thenReturn(true);
-        when(mStyleProvider.hasHeight()).thenReturn(true);
-        when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(width);
-        when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(height);
-
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(width);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(height);
-    }
-
-    @Test
-    public void createAdapter_resetsUnsetDimensions() {
-        // Set some dimensions first
-        int width = 10;
-        int height = 20;
-
-        when(mStyleProvider.hasWidth()).thenReturn(true);
-        when(mStyleProvider.hasHeight()).thenReturn(true);
-        when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(width);
-        when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(height);
-
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.releaseAdapter();
-
-        // Recreate with new style that unsets the dimensions
-        when(mStyleProvider.hasWidth()).thenReturn(false);
-        when(mStyleProvider.hasHeight()).thenReturn(false);
-        when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(DIMENSION_NOT_SET);
-        when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(DIMENSION_NOT_SET);
-
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void createAdapter_dimensionsDontOverrideSubclass() {
-        int width = 10;
-        int height = 20;
-
-        final int subclassWidth = 30;
-        final int subclassHeight = 40;
-
-        mAdapter = new TestElementAdapter(mContext, mParameters, mView) {
-            @Override
-            protected void onCreateAdapter(
-                    Object model, Element baseElement, FrameContext frameContext) {
-                mWidthPx = subclassWidth;
-                mHeightPx = subclassHeight;
-            }
-        };
-
-        when(mStyleProvider.hasWidth()).thenReturn(true);
-        when(mStyleProvider.hasHeight()).thenReturn(true);
-        when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(width);
-        when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(height);
-
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(subclassWidth);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(subclassHeight);
-    }
-
-    @Test
-    public void bindModel_callsOnBindModel() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-    }
-
-    @Test
-    public void testBindModel_callsOnBindLogDataCallback() {
-        Element defaultElement = Element.newBuilder().setLogData(mLogDataTest).build();
-
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-
-        assertThat(mCallbackBound).isTrue();
-    }
-
-    @Test
-    public void testBindModel_callsOnBindLogDataCallback_bindingRef() {
-        LogDataBindingRef logDataBinding =
-                LogDataBindingRef.newBuilder().setBindingId("LogData!").build();
-        Element defaultElement = Element.newBuilder().setLogDataRef(logDataBinding).build();
-        when(mFrameContext.getLogDataFromBinding(logDataBinding)).thenReturn(mLogDataTest);
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-
-        assertThat(mCallbackBound).isTrue();
-    }
-
-    @Test
-    public void bindModel_extractsModelFromElement() {
-        Element element = Element.getDefaultInstance();
-
-        mAdapter.bindModel(element, mFrameContext);
-
-        assertThat(mAdapter.getModel()).isEqualTo(TestElementAdapter.DEFAULT_MODEL);
-    }
-
-    @Test
-    public void testBindModel_setsActions() {
-        Actions actions =
-                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build();
-        Element elementWithActions = Element.newBuilder().setActions(actions).build();
-
-        mAdapter.bindModel(elementWithActions, elementWithActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isTrue();
-        assertThat(mAdapter.mActions).isSameInstanceAs(actions);
-    }
-
-    @Test
-    public void bindModel_setsActionsOnBaseView() {
-        Actions actions =
-                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build();
-        Element elementWithActions = Element.newBuilder().setActions(actions).build();
-        when(mStyleProvider.hasBorders()).thenReturn(true);
-
-        mAdapter.createAdapter(elementWithActions, elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, elementWithActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isFalse();
-        assertThat(mAdapter.getBaseView().hasOnClickListeners()).isTrue();
-        assertThat(mAdapter.mActions).isSameInstanceAs(actions);
-    }
-
-    @Test
-    public void bindModel_setsActionsWithBinding() {
-        Actions actions =
-                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build();
-        ActionsBindingRef actionsBinding =
-                ActionsBindingRef.newBuilder().setBindingId("ACTION!").build();
-        Element elementWithActions = Element.newBuilder().setActionsBinding(actionsBinding).build();
-        when(mFrameContext.getActionsFromBinding(actionsBinding)).thenReturn(actions);
-
-        mAdapter.bindModel(elementWithActions, elementWithActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isTrue();
-        assertThat(mAdapter.mActions).isSameInstanceAs(actions);
-    }
-
-    @Test
-    public void bindModel_unsetsActions() {
-        Actions actions =
-                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build();
-        Element elementWithActions = Element.newBuilder().setActions(actions).build();
-        Element elementWithoutActions = Element.getDefaultInstance();
-
-        mAdapter.bindModel(elementWithActions, elementWithActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isTrue();
-        assertThat(mAdapter.mActions).isSameInstanceAs(actions);
-
-        mAdapter.bindModel(elementWithoutActions, elementWithoutActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isFalse();
-        assertThat(mAdapter.mActions).isSameInstanceAs(Actions.getDefaultInstance());
-    }
-
-    @Test
-    public void bindModel_unsetsActionsOnBaseView() {
-        Actions actions =
-                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()).build();
-        Element elementWithActions = Element.newBuilder().setActions(actions).build();
-        Element elementWithoutActions = elementWithActions.toBuilder().clearActions().build();
-
-        when(mStyleProvider.hasBorders()).thenReturn(true);
-        mAdapter.createAdapter(elementWithActions, elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, elementWithActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isFalse();
-        assertThat(mAdapter.getBaseView().hasOnClickListeners()).isTrue();
-        assertThat(mAdapter.mActions).isSameInstanceAs(actions);
-
-        mAdapter.unbindModel();
-
-        when(mStyleProvider.hasBorders()).thenReturn(false);
-        mAdapter.bindModel(elementWithoutActions, elementWithoutActions, mFrameContext);
-        assertThat(mAdapter.getView().hasOnClickListeners()).isFalse();
-        assertThat(mAdapter.getBaseView().hasOnClickListeners()).isFalse();
-        assertThat(mAdapter.mActions).isSameInstanceAs(Actions.getDefaultInstance());
-    }
-
-    @Test
-    public void testBindModel_setsVisibility() {
-        Element visibleElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.VISIBLE))
-                        .build();
-        mAdapter.createAdapter(visibleElement, mFrameContext);
-        Element invisibleElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.INVISIBLE))
-                        .build();
-
-        mAdapter.bindModel(invisibleElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.INVISIBLE);
-    }
-
-    @Test
-    public void bindModel_doesNothingWhenVisibilityIsGone() {
-        Element visibleElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.VISIBLE))
-                        .build();
-        mAdapter.createAdapter(visibleElement, mFrameContext);
-        Element goneElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-
-        mAdapter.bindModel(goneElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterBound).isFalse();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void bindModel_recreatesWhenVisibilityWasGone() {
-        Element goneElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-        mAdapter.createAdapter(goneElement, mFrameContext);
-        Element invisibleElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.INVISIBLE))
-                        .build();
-
-        mAdapter.bindModel(invisibleElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.INVISIBLE);
-    }
-
-    @Test
-    public void bindModel_recreatesWhenVisibilityWasGone_bound() {
-        VisibilityBindingRef visibilityBinding =
-                VisibilityBindingRef.newBuilder().setBindingId("camo").build();
-        Element elementWithVisibilityBinding =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder()
-                                        .setDefaultVisibility(Visibility.GONE)
-                                        .setOverridingBoundVisibility(visibilityBinding))
-                        .build();
-
-        // When adapter is created, the Element is GONE
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding)).thenReturn(Visibility.GONE);
-
-        mAdapter.createAdapter(elementWithVisibilityBinding, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-
-        // Now on binding, the Element is VISIBLE
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding))
-                .thenReturn(Visibility.VISIBLE);
-
-        mAdapter.bindModel(elementWithVisibilityBinding, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void bindModel_doesntRecreateWhenWasCreatedButVisibilityGone() {
-        // Create a real adapter for a visible element.
-        Element visibileElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.VISIBLE))
-                        .build();
-        mAdapter.createAdapter(visibileElement, mFrameContext);
-
-        // Bind a gone element, and unbind it so the visibility is GONE.
-        Element goneElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-        mAdapter.bindModel(goneElement, mFrameContext);
-        mAdapter.unbindModel();
-
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-        mAdapter.mTestAdapterCreated =
-                false; // set this so we can test that it doesn't get recreated
-
-        // Bind a real element, and ensure that the adapter is not recreated.
-        Element invisibleElement =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.INVISIBLE))
-                        .build();
-
-        mAdapter.bindModel(invisibleElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.INVISIBLE);
-    }
-
-    @Test
-    public void bindModel_accessibility() {
-        Element accessibilityElement =
-                Element.newBuilder()
-                        .setAccessibility(
-                                Accessibility.newBuilder()
-                                        .addRoles(AccessibilityRole.HEADER)
-                                        .setDescription(ParameterizedText.newBuilder().setText(
-                                                "Accessible!")))
-                        .build();
-
-        mAdapter.bindModel(accessibilityElement, mFrameContext);
-        assertThat(mAdapter.getView().getContentDescription().toString()).isEqualTo("Accessible!");
-        final AccessibilityNodeInfo nodeInfo = AccessibilityNodeInfo.obtain();
-        mAdapter.getView().onInitializeAccessibilityNodeInfo(nodeInfo);
-        assertThat(nodeInfo.isHeading()).isTrue();
-    }
-
-    @Test
-    public void bindModel_accessibilityBinding() {
-        ParameterizedTextBindingRef textBinding =
-                ParameterizedTextBindingRef.newBuilder().setBindingId("binding").build();
-        Element accessibilityElement =
-                Element.newBuilder()
-                        .setAccessibility(
-                                Accessibility.newBuilder().setDescriptionBinding(textBinding))
-                        .build();
-        when(mFrameContext.getParameterizedTextBindingValue(textBinding))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setParameterizedText(
-                                            ParameterizedText.newBuilder().setText("Accessible!"))
-                                    .build());
-
-        mAdapter.bindModel(accessibilityElement, mFrameContext);
-
-        assertThat(mAdapter.getView().getContentDescription().toString()).isEqualTo("Accessible!");
-    }
-
-    @Test
-    public void bindModel_accessibilityReset() {
-        mAdapter.getView().setContentDescription("OLD CONTENT DESCRIPTION");
-
-        mAdapter.bindModel(Element.getDefaultInstance(), mFrameContext);
-
-        assertThat(mAdapter.getView().getContentDescription()).isNull();
-    }
-
-    @Test
-    public void unbindModel_callsOnUnbindModel() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-
-        mAdapter.unbindModel();
-        assertThat(mAdapter.mTestAdapterBound).isFalse();
-    }
-
-    @Test
-    public void testUnbindModel_callsOnBindLogDataCallback() {
-        Element defaultElement = Element.newBuilder().setLogData(mLogDataTest).build();
-
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-        mAdapter.unbindModel();
-        assertThat(mCallbackUnbound).isTrue();
-    }
-
-    @Test
-    public void testUnbindModel_callsOnBindLogDataCallback_bindingRef() {
-        LogDataBindingRef logDataBinding =
-                LogDataBindingRef.newBuilder().setBindingId("LogData!").build();
-        Element defaultElement = Element.newBuilder().setLogDataRef(logDataBinding).build();
-        when(mFrameContext.getLogDataFromBinding(logDataBinding)).thenReturn(mLogDataTest);
-
-        mAdapter.bindModel(defaultElement, defaultElement, mFrameContext);
-        mAdapter.unbindModel();
-        assertThat(mCallbackUnbound).isTrue();
-    }
-
-    @Test
-    public void unbindModel_unsetsModel() {
-        Element element = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(element, element, mFrameContext);
-        mAdapter.bindModel(element, element, mFrameContext);
-        assertThat(mAdapter.getModel()).isEqualTo(element);
-
-        mAdapter.unbindModel();
-        assertThat(mAdapter.getRawModel()).isNull();
-    }
-
-    // If onBindModel was never called, onUnbindModel should not be called.
-    @Test
-    public void unbindModel_visibilityWasGone() {
-        Element goneElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-        mAdapter.createAdapter(goneElement, mFrameContext);
-        mAdapter.bindModel(goneElement, mFrameContext);
-
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-        assertThat(mAdapter.mTestAdapterBound).isFalse();
-
-        // Set this to something else so we can check if onUnbindModel gets called - it should not.
-        mAdapter.mTestAdapterBound = true;
-
-        mAdapter.unbindModel();
-
-        // onUnbindModel was not called so this is still true.
-        assertThat(mAdapter.mTestAdapterBound).isTrue();
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-    }
-
-    @Test
-    public void unbindModel_unsetsActions() {
-        Element elementWithWrapperAndActions =
-                Element.newBuilder()
-                        .setActions(
-                                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()))
-                        .build();
-
-        when(mStyleProvider.hasBorders()).thenReturn(true);
-
-        mAdapter.createAdapter(
-                elementWithWrapperAndActions, elementWithWrapperAndActions, mFrameContext);
-        mAdapter.bindModel(
-                elementWithWrapperAndActions, elementWithWrapperAndActions, mFrameContext);
-        View adapterView = mAdapter.getBaseView();
-        View wrapperView = mAdapter.getView();
-
-        mAdapter.unbindModel();
-
-        assertThat(adapterView.hasOnClickListeners()).isFalse();
-        assertThat(wrapperView.hasOnClickListeners()).isFalse();
-    }
-
-    @Test
-    public void testUnbindModel_affectsVisibilityCalculations() {
-        VisibilityBindingRef visibilityBinding =
-                VisibilityBindingRef.newBuilder().setBindingId("vis").build();
-        Element element = Element.newBuilder()
-                                  .setVisibilityState(
-                                          VisibilityState.newBuilder()
-                                                  .setDefaultVisibility(Visibility.INVISIBLE)
-                                                  .setOverridingBoundVisibility(visibilityBinding))
-                                  .build();
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding))
-                .thenReturn(Visibility.VISIBLE);
-
-        mAdapter.createAdapter(element, element, mFrameContext);
-        assertThat(mAdapter.getVisibilityForElement(element, mFrameContext))
-                .isEqualTo(Visibility.INVISIBLE);
-
-        mAdapter.bindModel(element, element, mFrameContext);
-        assertThat(mAdapter.getVisibilityForElement(element, mFrameContext))
-                .isEqualTo(Visibility.VISIBLE);
-
-        mAdapter.unbindModel();
-        assertThat(mAdapter.getVisibilityForElement(element, mFrameContext))
-                .isEqualTo(Visibility.INVISIBLE);
-    }
-
-    @Test
-    public void releaseAdapter_callsOnReleaseAdapter() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-        assertThat(mAdapter.mTestAdapterCreated).isTrue();
-
-        mAdapter.releaseAdapter();
-        assertThat(mAdapter.mTestAdapterCreated).isFalse();
-    }
-
-    @Test
-    public void testReleaseAdapter_resetsVisibility() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-        mAdapter.setVisibilityOnView(Visibility.INVISIBLE);
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.INVISIBLE);
-
-        mAdapter.releaseAdapter();
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void releaseAdapter_resetsDimensions() {
-        Element defaultElement = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(defaultElement, defaultElement, mFrameContext);
-        mAdapter.setDims(123, 456);
-
-        mAdapter.releaseAdapter();
-
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void setVisibility() {
-        mAdapter.createAdapter(
-                Element.getDefaultInstance(), Element.getDefaultInstance(), mFrameContext);
-        mAdapter.getBaseView().setVisibility(View.GONE);
-        mAdapter.setVisibilityOnView(Visibility.VISIBLE);
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.VISIBLE);
-        mAdapter.setVisibilityOnView(Visibility.INVISIBLE);
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.INVISIBLE);
-        mAdapter.setVisibilityOnView(Visibility.GONE);
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.GONE);
-        mAdapter.setVisibilityOnView(Visibility.VISIBILITY_UNSPECIFIED);
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void getVisibilityForElement() {
-        VisibilityBindingRef bindingRef =
-                VisibilityBindingRef.newBuilder().setBindingId("binding").build();
-        Element elementWithDefaultVisibility =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder().setDefaultVisibility(
-                                Visibility.INVISIBLE))
-                        .build();
-        Element elementWithVisibilityOverride =
-                Element.newBuilder()
-                        .setVisibilityState(VisibilityState.newBuilder()
-                                                    .setDefaultVisibility(Visibility.INVISIBLE)
-                                                    .setOverridingBoundVisibility(bindingRef))
-                        .build();
-
-        // Default visibility; no override
-        assertThat(mAdapter.getVisibilityForElement(elementWithDefaultVisibility, mFrameContext))
-                .isEqualTo(Visibility.INVISIBLE);
-
-        // Don't look up override when not binding
-        when(mFrameContext.getVisibilityFromBinding(bindingRef)).thenReturn(Visibility.GONE);
-        assertThat(mAdapter.getVisibilityForElement(elementWithVisibilityOverride, mFrameContext))
-                .isEqualTo(Visibility.INVISIBLE);
-
-        mAdapter.bindModel(Element.getDefaultInstance(), mFrameContext);
-
-        // Look up override successfully
-        when(mFrameContext.getVisibilityFromBinding(bindingRef)).thenReturn(Visibility.GONE);
-        assertThat(mAdapter.getVisibilityForElement(elementWithVisibilityOverride, mFrameContext))
-                .isEqualTo(Visibility.GONE);
-
-        // Override not found; use default
-        when(mFrameContext.getVisibilityFromBinding(bindingRef)).thenReturn(null);
-        assertThat(mAdapter.getVisibilityForElement(elementWithVisibilityOverride, mFrameContext))
-                .isEqualTo(Visibility.INVISIBLE);
-    }
-
-    @Config(shadows = {ExtendedShadowView.class})
-    @Test
-    public void getOnFullViewActions() {
-        Frame frame = Frame.newBuilder().setTag("FRAME").build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        View viewport = new View(mContext);
-
-        // No actions defined
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.bindModel(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.releaseAdapter();
-
-        // Actions defined, but not fullview actions
-        Element elementWithNoViewActions =
-                Element.newBuilder()
-                        .setActions(
-                                Actions.newBuilder().setOnClickAction(Action.newBuilder().build()))
-                        .build();
-        mAdapter.createAdapter(elementWithNoViewActions, mFrameContext);
-        mAdapter.bindModel(elementWithNoViewActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        // Actions defined, but not fully visible
-        ExtendedShadowView viewShadow = Shadow.extract(mAdapter.getView());
-        viewShadow.setAttachedToWindow(false);
-        Actions fullViewActions = Actions.newBuilder()
-                                          .addOnViewActions(VisibilityAction.newBuilder().setAction(
-                                                  Action.newBuilder().build()))
-                                          .build();
-        Element elementWithActions = Element.newBuilder().setActions(fullViewActions).build();
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        // Actions defined, and fully visible
-        ExtendedShadowView viewportShadow = Shadow.extract(viewport);
-        viewportShadow.setLocationInWindow(0, 0);
-        viewportShadow.setHeight(100);
-        viewportShadow.setWidth(100);
-        viewShadow.setLocationInWindow(10, 10);
-        viewShadow.setHeight(50);
-        viewShadow.setWidth(50);
-        viewShadow.setAttachedToWindow(true);
-
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verify(mActionHandler)
-                .handleAction(same(fullViewActions.getOnViewActions(0).getAction()),
-                        eq(ActionType.VIEW), eq(frame), eq(mView),
-                        eq(LogData.getDefaultInstance()));
-    }
-
-    @Config(shadows = {ExtendedShadowView.class})
-    @Test
-    public void getOnPartialViewActions() {
-        Frame frame = Frame.newBuilder().setTag("FRAME").build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        View viewport = new View(mContext);
-
-        // No actions defined
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.bindModel(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.releaseAdapter();
-
-        // Actions defined, but not partial view actions
-        Element elementWithNoViewActions =
-                Element.newBuilder()
-                        .setActions(
-                                Actions.newBuilder().setOnClickAction(Action.newBuilder().build()))
-                        .build();
-        mAdapter.createAdapter(elementWithNoViewActions, mFrameContext);
-        mAdapter.bindModel(elementWithNoViewActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        // Actions defined, but not attached to window
-        ExtendedShadowView viewShadow = Shadow.extract(mAdapter.getView());
-        viewShadow.setAttachedToWindow(false);
-        Actions partialViewActions =
-                Actions.newBuilder()
-                        .addOnViewActions(VisibilityAction.newBuilder()
-                                                  .setAction(Action.newBuilder().build())
-                                                  .setProportionVisible(0.01f))
-                        .build();
-        Element elementWithActions = Element.newBuilder().setActions(partialViewActions).build();
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        // Actions defined, and partially visible
-        ExtendedShadowView viewportShadow = Shadow.extract(viewport);
-        viewportShadow.setLocationInWindow(0, 0);
-        viewportShadow.setHeight(100);
-        viewportShadow.setWidth(100);
-        viewShadow.setLocationInWindow(10, 10);
-        viewShadow.setHeight(200);
-        viewShadow.setWidth(200);
-        viewShadow.setAttachedToWindow(true);
-
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verify(mActionHandler)
-                .handleAction(same(partialViewActions.getOnViewActions(0).getAction()),
-                        eq(ActionType.VIEW), eq(frame), eq(mView),
-                        eq(LogData.getDefaultInstance()));
-
-        // Actions defined, and fully visible
-        viewShadow.setHeight(50);
-        viewShadow.setWidth(50);
-
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verify(mActionHandler, times(2))
-                .handleAction(same(partialViewActions.getOnViewActions(0).getAction()),
-                        eq(ActionType.VIEW), eq(frame), eq(mView),
-                        eq(LogData.getDefaultInstance()));
-    }
-
-    @Config(shadows = {ExtendedShadowView.class})
-    @Test
-    public void triggerHideActions() {
-        Frame frame = Frame.newBuilder().setTag("FRAME").build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        View viewport = new View(mContext);
-
-        // No actions defined
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.bindModel(Element.getDefaultInstance(), mFrameContext);
-        mAdapter.triggerHideActions(mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.releaseAdapter();
-
-        // Actions defined, but not hide actions
-        Element elementWithNoViewActions =
-                Element.newBuilder()
-                        .setActions(
-                                Actions.newBuilder().setOnClickAction(Action.getDefaultInstance()))
-                        .build();
-        mAdapter.createAdapter(elementWithNoViewActions, mFrameContext);
-        mAdapter.bindModel(elementWithNoViewActions, mFrameContext);
-        mAdapter.triggerHideActions(mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        // Trigger hide actions on the fully-visible view to make hide actions not active.
-        Action hideAction = Action.newBuilder().build();
-        Actions hideActions =
-                Actions.newBuilder()
-                        .addOnHideActions(VisibilityAction.newBuilder().setAction(hideAction))
-                        .build();
-        Element elementWithActions = Element.newBuilder().setActions(hideActions).build();
-        ExtendedShadowView viewShadow = Shadow.extract(mAdapter.getView());
-        ExtendedShadowView viewportShadow = Shadow.extract(viewport);
-        viewportShadow.setLocationInWindow(0, 0);
-        viewportShadow.setHeight(100);
-        viewportShadow.setWidth(100);
-        viewShadow.setLocationInWindow(10, 10);
-        viewShadow.setHeight(50);
-        viewShadow.setWidth(50);
-        viewShadow.setAttachedToWindow(true);
-
-        mAdapter.createAdapter(elementWithActions, mFrameContext);
-        mAdapter.bindModel(elementWithActions, mFrameContext);
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-        verifyZeroInteractions(mActionHandler);
-
-        // Trigger hide actions
-        mAdapter.triggerHideActions(mFrameContext);
-
-        verify(mActionHandler)
-                .handleAction(same(hideAction), eq(ActionType.VIEW), eq(frame), eq(mView),
-                        eq(LogData.getDefaultInstance()));
-
-        // Ensure actions do not trigger again
-        mAdapter.triggerHideActions(mFrameContext);
-
-        verify(mActionHandler) // Only once, not twice
-                .handleAction(same(hideAction), eq(ActionType.VIEW), eq(frame), eq(mView),
-                        eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void createRoundedCorners() {
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
-        RoundedCornerWrapperView roundedCornerWrapperView = new RoundedCornerWrapperView(mContext,
-                roundedCorners, mMaskCache, Suppliers.of(false),
-                /*radiusOverride= */ 0, Borders.getDefaultInstance(), LEGACY_CORNERS_FLAG,
-                OUTLINE_CORNERS_FLAG);
-        when(mStyleProvider.hasRoundedCorners()).thenReturn(true);
-        when(mStyleProvider.createWrapperView(
-                     mContext, mMaskCache, LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG))
-                .thenReturn(roundedCornerWrapperView);
-
-        mAdapter.createAdapter(Element.getDefaultInstance(), mFrameContext);
-
-        assertThat(mAdapter.getView()).isSameInstanceAs(roundedCornerWrapperView);
-    }
-
-    @Test
-    public void setVisibilityWithRoundedCorners() {
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
-        RoundedCornerWrapperView roundedCornerWrapperView = new RoundedCornerWrapperView(mContext,
-                roundedCorners, mMaskCache, Suppliers.of(false),
-                /*radiusOverride= */ 0, Borders.getDefaultInstance(), LEGACY_CORNERS_FLAG,
-                OUTLINE_CORNERS_FLAG);
-        when(mStyleProvider.hasRoundedCorners()).thenReturn(true);
-        when(mStyleProvider.createWrapperView(
-                     mContext, mMaskCache, LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG))
-                .thenReturn(roundedCornerWrapperView);
-
-        VisibilityBindingRef visibilityBinding =
-                VisibilityBindingRef.newBuilder().setBindingId("visibility").build();
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding))
-                .thenReturn(Visibility.VISIBLE);
-
-        Element element = Element.newBuilder()
-                                  .setVisibilityState(
-                                          VisibilityState.newBuilder()
-                                                  .setDefaultVisibility(Visibility.GONE)
-                                                  .setOverridingBoundVisibility(visibilityBinding))
-                                  .build();
-
-        mAdapter.createAdapter(element, mFrameContext);
-
-        FrameLayout parentView = new FrameLayout(mContext);
-        parentView.addView(mAdapter.getView());
-
-        mAdapter.bindModel(element, mFrameContext);
-
-        assertThat(mAdapter.getView()).isSameInstanceAs(roundedCornerWrapperView);
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.VISIBLE);
-
-        // Try setting visibility back to GONE and ensure that RCWV is GONE
-        when(mFrameContext.getVisibilityFromBinding(visibilityBinding)).thenReturn(Visibility.GONE);
-        mAdapter.unbindModel();
-        mAdapter.bindModel(element, mFrameContext);
-        assertThat(mAdapter.getView()).isSameInstanceAs(roundedCornerWrapperView);
-        assertThat(mAdapter.getView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void getStyleIdsStack() {
-        StyleIdsStack style = StyleIdsStack.newBuilder().addStyleIds("style").build();
-        Element elementWithStyle = Element.newBuilder().setStyleReferences(style).build();
-
-        mAdapter.createAdapter(elementWithStyle, mFrameContext);
-
-        assertThat(mAdapter.getElementStyleIdsStack()).isSameInstanceAs(style);
-
-        Element elementWithNoStyle = Element.getDefaultInstance();
-
-        mAdapter.createAdapter(elementWithNoStyle, mFrameContext);
-
-        assertThat(mAdapter.getElementStyleIdsStack())
-                .isEqualTo(StyleIdsStack.getDefaultInstance());
-    }
-
-    // Dummy implementation
-    static class TestElementAdapter extends ElementAdapter<View, Object> {
-        static final String DEFAULT_MODEL = "MODEL";
-
-        boolean mTestAdapterCreated;
-        boolean mTestAdapterBound;
-
-        View mAdapterView;
-
-        TestElementAdapter(Context context, AdapterParameters parameters, View adapterView) {
-            super(context, parameters, adapterView);
-            this.mAdapterView = adapterView;
-        }
-
-        @Override
-        protected Object getModelFromElement(Element baseElement) {
-            return DEFAULT_MODEL;
-        }
-
-        @Override
-        protected void onCreateAdapter(
-                Object model, Element baseElement, FrameContext frameContext) {
-            mTestAdapterCreated = true;
-        }
-
-        @Override
-        protected void onBindModel(Object model, Element baseElement, FrameContext frameContext) {
-            mTestAdapterBound = true;
-        }
-
-        @Override
-        protected void onUnbindModel() {
-            mTestAdapterBound = false;
-        }
-
-        @Override
-        protected void onReleaseAdapter() {
-            mTestAdapterCreated = false;
-        }
-
-        public void setDims(int width, int height) {
-            mWidthPx = width;
-            mHeightPx = height;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementContainerAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementContainerAdapterTest.java
deleted file mode 100644
index ced051f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementContainerAdapterTest.java
+++ /dev/null
@@ -1,648 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ElementBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.TemplateBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingContext;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TemplateInvocation;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.VisibilityState;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests of the {@link ElementContainerAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ElementContainerAdapterTest {
-    private static final Element DUMMY_BASE_ELEMENT = Element.getDefaultInstance();
-    private static final Element DEFAULT_ELEMENT =
-            Element.newBuilder().setElementList(ElementList.newBuilder().build()).build();
-
-    private static final Element DEFAULT_BOUND_ELEMENT =
-            Element.newBuilder().setGridRow(GridRow.getDefaultInstance()).build();
-    private static final String DEFAULT_ELEMENT_BINDING_ID = "bound-grid-element";
-    private static final ElementBindingRef DEFAULT_ELEMENT_BINDING =
-            ElementBindingRef.newBuilder().setBindingId(DEFAULT_ELEMENT_BINDING_ID).build();
-    private static final BindingValue DEFAULT_ELEMENT_BINDING_VALUE =
-            BindingValue.newBuilder()
-                    .setBindingId(DEFAULT_ELEMENT_BINDING_ID)
-                    .setElement(DEFAULT_BOUND_ELEMENT)
-                    .build();
-
-    private static final String DEFAULT_TEMPLATE_ID = "inline-text-template";
-    private static final Template DEFAULT_TEMPLATE =
-            Template.newBuilder()
-                    .setTemplateId(DEFAULT_TEMPLATE_ID)
-                    .setElement(Element.newBuilder().setTextElement(
-                            TextElement.newBuilder().setParameterizedText(
-                                    ParameterizedText.getDefaultInstance())))
-                    .build();
-    private static final TemplateInvocation DEFAULT_TEMPLATE_INVOCATION =
-            TemplateInvocation.newBuilder()
-                    .setTemplateId(DEFAULT_TEMPLATE_ID)
-                    .addBindingContexts(BindingContext.newBuilder().addBindingValues(
-                            BindingValue.newBuilder().setBindingId("T1")))
-                    .addBindingContexts(BindingContext.newBuilder().addBindingValues(
-                            BindingValue.newBuilder().setBindingId("T2")))
-                    .build();
-
-    private static final String DEFAULT_BOUND_TEMPLATE_ID = "bound-image-template";
-    private static final Template DEFAULT_BOUND_TEMPLATE =
-            Template.newBuilder()
-                    .setTemplateId(DEFAULT_BOUND_TEMPLATE_ID)
-                    .setElement(Element.newBuilder().setImageElement(
-                            ImageElement.newBuilder().setImage(Image.getDefaultInstance())))
-                    .build();
-    private static final TemplateInvocation DEFAULT_BOUND_TEMPLATE_INVOCATION =
-            TemplateInvocation.newBuilder()
-                    .setTemplateId(DEFAULT_BOUND_TEMPLATE_ID)
-                    .addBindingContexts(BindingContext.newBuilder().addBindingValues(
-                            BindingValue.newBuilder().setBindingId("BT1")))
-                    .addBindingContexts(BindingContext.newBuilder().addBindingValues(
-                            BindingValue.newBuilder().setBindingId("BT2")))
-                    .addBindingContexts(BindingContext.newBuilder().addBindingValues(
-                            BindingValue.newBuilder().setBindingId("BT3")))
-                    .build();
-    private static final String DEFAULT_TEMPLATE_BINDING_ID = "bind-the-bound-image-template";
-    private static final TemplateBindingRef DEFAULT_TEMPLATE_BINDING =
-            TemplateBindingRef.newBuilder().setBindingId(DEFAULT_TEMPLATE_BINDING_ID).build();
-    private static final BindingValue TEMPLATE_BINDING_VALUE =
-            BindingValue.newBuilder()
-                    .setBindingId(DEFAULT_TEMPLATE_BINDING_ID)
-                    .setTemplateInvocation(DEFAULT_BOUND_TEMPLATE_INVOCATION)
-                    .build();
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private Context mContext;
-
-    private TestElementContainerAdapter mAdapter;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        AdapterParameters adapterParameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mHostProviders, new FakeClock(), false, false);
-
-        when(mFrameContext.createTemplateContext(eq(DEFAULT_TEMPLATE), any(BindingContext.class)))
-                .thenReturn(mFrameContext);
-        when(mFrameContext.createTemplateContext(
-                     eq(DEFAULT_BOUND_TEMPLATE), any(BindingContext.class)))
-                .thenReturn(mFrameContext);
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class)))
-                .thenReturn(adapterParameters.mDefaultStyleProvider);
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
-                .thenAnswer(invocation -> invocation.getArguments()[0]);
-
-        // Default no-errors behavior
-        when(mFrameContext.getTemplateInvocationBindingValue(DEFAULT_TEMPLATE_BINDING))
-                .thenReturn(TEMPLATE_BINDING_VALUE);
-        when(mFrameContext.getTemplate(DEFAULT_TEMPLATE_ID)).thenReturn(DEFAULT_TEMPLATE);
-        when(mFrameContext.getTemplate(DEFAULT_BOUND_TEMPLATE_ID))
-                .thenReturn(DEFAULT_BOUND_TEMPLATE);
-        when(mFrameContext.getElementBindingValue(DEFAULT_ELEMENT_BINDING))
-                .thenReturn(DEFAULT_ELEMENT_BINDING_VALUE);
-
-        LinearLayout view = new LinearLayout(mContext);
-
-        mAdapter = new TestElementContainerAdapter(mContext, adapterParameters, view);
-    }
-
-    @Test
-    public void testCreateChildAdapters_elementCreatesOneAdapter() {
-        mAdapter.createAdapter(
-                Collections.singletonList(Content.newBuilder().setElement(DEFAULT_ELEMENT).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // A single adapter created from the Element.
-        assertThat(mAdapter.mChildAdapters).hasSize(1);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(ElementListAdapter.class);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {1});
-    }
-
-    @Test
-    public void testCreateChildAdapters_templateCreatesMultipleAdapters() {
-        mAdapter.createAdapter(Collections.singletonList(
-                                       Content.newBuilder()
-                                               .setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION)
-                                               .build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // One TemplateInstanceAdapter for each BindingContext in the TemplateInvocation.
-        assertThat(DEFAULT_TEMPLATE_INVOCATION.getBindingContextsCount()).isEqualTo(2);
-        assertThat(mAdapter.mChildAdapters).hasSize(2);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(TextElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(1)).isInstanceOf(TextElementAdapter.class);
-        verify(mFrameContext)
-                .createTemplateContext(
-                        DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(0));
-        verify(mFrameContext)
-                .createTemplateContext(
-                        DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(1));
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(2);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-        assertThat(mAdapter.getBaseView().getChildAt(1))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(1).getView());
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {2});
-    }
-
-    @Test
-    public void testCreateChildAdapters_templateNotFound() {
-        when(mFrameContext.getTemplate(DEFAULT_TEMPLATE_ID)).thenReturn(null);
-
-        mAdapter.createAdapter(Collections.singletonList(
-                                       Content.newBuilder()
-                                               .setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION)
-                                               .build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testCreateChildAdapters_boundElement_doesNothing() {
-        mAdapter.createAdapter(
-                Collections.singletonList(
-                        Content.newBuilder().setBoundElement(DEFAULT_ELEMENT_BINDING).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testCreateChildAdapters_boundElementNotFound_doesNothing() {
-        when(mFrameContext.getElementBindingValue(DEFAULT_ELEMENT_BINDING))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.createAdapter(
-                Collections.singletonList(
-                        Content.newBuilder().setBoundElement(DEFAULT_ELEMENT_BINDING).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testCreateChildAdapters_boundTemplate_doesNothing() {
-        mAdapter.createAdapter(
-                Collections.singletonList(
-                        Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testCreateChildAdapters_boundTemplateNotFound_doesNothing() {
-        when(mFrameContext.getTemplateInvocationBindingValue(DEFAULT_TEMPLATE_BINDING))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.createAdapter(
-                Collections.singletonList(
-                        Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testCreateChildAdapters_boundTemplateBetweenInlineContents() {
-        List<Content> contents = new ArrayList<>();
-        contents.add(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        contents.add(Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        contents.add(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // Adapters created:  2 templates, (nothing for the binding), 1 element list
-        assertThat(mAdapter.mChildAdapters).hasSize(3);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(TextElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(1)).isInstanceOf(TextElementAdapter.class);
-
-        assertThat(mAdapter.mChildAdapters.get(2)).isInstanceOf(ElementListAdapter.class);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(3);
-        for (int i = 0; i < 3; i++) {
-            assertThat(mAdapter.getBaseView().getChildAt(i))
-                    .isSameInstanceAs(mAdapter.mChildAdapters.get(i).getView());
-        }
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {2, 0, 1});
-    }
-
-    @Test
-    public void testCreateChildAdapters_failsIfAdapterNotReleased() {
-        mAdapter.createAdapter(
-                Collections.singletonList(Content.newBuilder().setElement(DEFAULT_ELEMENT).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThatRunnable(
-                ()
-                        -> mAdapter.createAdapter(
-                                Collections.singletonList(
-                                        Content.newBuilder().setElement(DEFAULT_ELEMENT).build()),
-                                DUMMY_BASE_ELEMENT, mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("release adapter before creating");
-    }
-
-    @Test
-    public void testBindChildAdapters_createsAdapterIfNotAlreadyCreated() {
-        mAdapter.bindModel(
-                Collections.singletonList(Content.newBuilder().setElement(DEFAULT_ELEMENT).build()),
-                DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.mChildAdapters).hasSize(1);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(ElementListAdapter.class);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void testBindChildAdapters_element() {
-        List<Content> contents =
-                Collections.singletonList(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        ElementAdapter<? extends View, ?> mockChildAdapter = mock(ElementListAdapter.class);
-        mAdapter.mChildAdapters.set(0, mockChildAdapter);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        verify(mockChildAdapter).bindModel(DEFAULT_ELEMENT, mFrameContext);
-    }
-
-    @Test
-    public void testBindChildAdapters_template() {
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        RecyclerKey key1 = mAdapter.mChildAdapters.get(0).getKey();
-        RecyclerKey key2 = mAdapter.mChildAdapters.get(1).getKey();
-        TextElementAdapter mockChildAdapter1 = mock(TextElementAdapter.class);
-        TextElementAdapter mockChildAdapter2 = mock(TextElementAdapter.class);
-        when(mockChildAdapter1.getKey()).thenReturn(key1);
-        when(mockChildAdapter2.getKey()).thenReturn(key2);
-        mAdapter.mChildAdapters.set(0, mockChildAdapter1);
-        mAdapter.mChildAdapters.set(1, mockChildAdapter2);
-
-        FrameContext templateContext1 = mock(FrameContext.class);
-        FrameContext templateContext2 = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(
-                     DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(0)))
-                .thenReturn(templateContext1);
-        when(mFrameContext.createTemplateContext(
-                     DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(1)))
-                .thenReturn(templateContext2);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        verify(mockChildAdapter1).bindModel(DEFAULT_TEMPLATE.getElement(), templateContext1);
-        verify(mockChildAdapter2).bindModel(DEFAULT_TEMPLATE.getElement(), templateContext2);
-    }
-
-    @Test
-    public void testBindChildAdapters_templateNotFound() {
-        when(mFrameContext.getTemplate(DEFAULT_TEMPLATE_ID)).thenReturn(null);
-
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // No child adapters are created; binding does nothing.
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testBindChildAdapters_boundElement() {
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setBoundElement(DEFAULT_ELEMENT_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {1});
-    }
-
-    @Test
-    public void testBindChildAdapters_boundTemplate() {
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(3);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-        assertThat(mAdapter.getBaseView().getChildAt(1))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(1).getView());
-        assertThat(mAdapter.getBaseView().getChildAt(2))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(2).getView());
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {3});
-    }
-
-    @Test
-    public void testBindChildAdapters_boundTemplateNotFound_doesNothing() {
-        when(mFrameContext.getTemplate(DEFAULT_BOUND_TEMPLATE_ID)).thenReturn(null);
-
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testBindChildAdapters_boundTemplateBindingNotFound_doesNothing() {
-        when(mFrameContext.getTemplateInvocationBindingValue(DEFAULT_TEMPLATE_BINDING))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testBindChildAdapters_boundTemplateBetweenInlineContents() {
-        List<Content> contents = new ArrayList<>();
-        contents.add(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        contents.add(Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        contents.add(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        // 2 templates, 3 templates, 1 element list
-        assertThat(mAdapter.mChildAdapters).hasSize(6);
-
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(TextElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(1)).isInstanceOf(TextElementAdapter.class);
-
-        assertThat(mAdapter.mChildAdapters.get(2)).isInstanceOf(ImageElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(3)).isInstanceOf(ImageElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(4)).isInstanceOf(ImageElementAdapter.class);
-
-        assertThat(mAdapter.mChildAdapters.get(5)).isInstanceOf(ElementListAdapter.class);
-
-        // Each of these occur 2 times; once for create and once for bind.
-        verify(mFrameContext, times(2))
-                .createTemplateContext(
-                        DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(0));
-        verify(mFrameContext, times(2))
-                .createTemplateContext(
-                        DEFAULT_TEMPLATE, DEFAULT_TEMPLATE_INVOCATION.getBindingContexts(1));
-
-        // These only occur once because we do create and bind at the same time.
-        verify(mFrameContext)
-                .createTemplateContext(DEFAULT_BOUND_TEMPLATE,
-                        DEFAULT_BOUND_TEMPLATE_INVOCATION.getBindingContexts(0));
-        verify(mFrameContext)
-                .createTemplateContext(DEFAULT_BOUND_TEMPLATE,
-                        DEFAULT_BOUND_TEMPLATE_INVOCATION.getBindingContexts(1));
-        verify(mFrameContext)
-                .createTemplateContext(DEFAULT_BOUND_TEMPLATE,
-                        DEFAULT_BOUND_TEMPLATE_INVOCATION.getBindingContexts(2));
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(6);
-        for (int i = 0; i < 6; i++) {
-            assertThat(mAdapter.getBaseView().getChildAt(i))
-                    .isSameInstanceAs(mAdapter.mChildAdapters.get(i).getView());
-        }
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {2, 3, 1});
-    }
-
-    @Test
-    public void testUnbindChildAdapters_inline() {
-        List<Content> contents = new ArrayList<>();
-        contents.add(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        contents.add(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        // 2 templates, 1 element list
-        assertThat(mAdapter.mChildAdapters).hasSize(3);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(TextElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(1)).isInstanceOf(TextElementAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(2)).isInstanceOf(ElementListAdapter.class);
-
-        assertThat(mAdapter.mChildAdapters.get(0).getRawModel()).isNull();
-        assertThat(mAdapter.mChildAdapters.get(1).getRawModel()).isNull();
-        assertThat(mAdapter.mChildAdapters.get(2).getRawModel()).isNull();
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(3);
-        for (int i = 0; i < 3; i++) {
-            assertThat(mAdapter.getBaseView().getChildAt(i))
-                    .isSameInstanceAs(mAdapter.mChildAdapters.get(i).getView());
-        }
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {2, 1});
-    }
-
-    @Test
-    public void testUnbindChildAdapters_boundElement() {
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setBoundElement(DEFAULT_ELEMENT_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        // All (bound) adapters and views have been destroyed
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testUnbindChildAdapters_boundTemplate() {
-        List<Content> contents = Collections.singletonList(
-                Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        mAdapter.unbindModel();
-
-        // All (bound) adapters and views have been destroyed
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {0});
-    }
-
-    @Test
-    public void testUnbindChildAdapters_boundContentBetweenInlineContents() {
-        List<Content> contents = new ArrayList<>();
-        contents.add(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        contents.add(Content.newBuilder().setTemplateBinding(DEFAULT_TEMPLATE_BINDING).build());
-        contents.add(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-        mAdapter.bindModel(contents, DUMMY_BASE_ELEMENT, mFrameContext);
-
-        assertThat(mAdapter.mChildAdapters).hasSize(6);
-        ImageElementAdapter mockTemplateAdapter = mock(ImageElementAdapter.class);
-        mAdapter.mChildAdapters.set(3, mockTemplateAdapter);
-
-        mAdapter.unbindModel();
-
-        // The inline adapters have been unbound; the bound adapters have been destroyed.
-        assertThat(mAdapter.mChildAdapters).hasSize(3);
-        assertThat(mAdapter.mChildAdapters.get(0).getRawModel()).isNull();
-        assertThat(mAdapter.mChildAdapters.get(1).getRawModel()).isNull();
-        assertThat(mAdapter.mChildAdapters.get(2).getRawModel()).isNull();
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(3);
-        for (int i = 0; i < 3; i++) {
-            assertThat(mAdapter.getBaseView().getChildAt(i))
-                    .isSameInstanceAs(mAdapter.mChildAdapters.get(i).getView());
-        }
-
-        assertThat(mAdapter.mAdaptersPerContent).isEqualTo(new int[] {2, 0, 1});
-
-        verify(mockTemplateAdapter).unbindModel();
-        verify(mockTemplateAdapter).releaseAdapter();
-    }
-
-    @Test
-    public void testUnbind_visibilityWasGone() {
-        List<Content> contents = new ArrayList<>();
-        contents.add(
-                Content.newBuilder().setTemplateInvocation(DEFAULT_TEMPLATE_INVOCATION).build());
-        contents.add(Content.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        Element goneElement =
-                Element.newBuilder()
-                        .setVisibilityState(
-                                VisibilityState.newBuilder().setDefaultVisibility(Visibility.GONE))
-                        .build();
-
-        mAdapter.createAdapter(contents, goneElement, mFrameContext);
-        mAdapter.bindModel(contents, goneElement, mFrameContext);
-
-        // Nothing was created because the visibility was GONE
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEmpty();
-
-        mAdapter.unbindModel();
-
-        // Nothing crashes, and the adapter is still uncreated.
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-        assertThat(mAdapter.mAdaptersPerContent).isEmpty();
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-    }
-
-    private static class TestElementContainerAdapter
-            extends ElementContainerAdapter<LinearLayout, List<Content>> {
-        TestElementContainerAdapter(
-                Context context, AdapterParameters adapterParameters, LinearLayout view) {
-            super(context, adapterParameters, view);
-        }
-
-        @Override
-        List<Content> getContentsFromModel(List<Content> model) {
-            return model;
-        }
-
-        @Override
-        List<Content> getModelFromElement(Element baseElement) {
-            throw new UnsupportedOperationException("Not used in this test");
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementListAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementListAdapterTest.java
deleted file mode 100644
index 86569ea..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementListAdapterTest.java
+++ /dev/null
@@ -1,534 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.ElementListAdapter.KeySupplier;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.testing.shadows.ExtendedShadowLinearLayout;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ElementBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementStack;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ElementListAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ElementListAdapterTest {
-    private static final String LIST_STYLE_ID = "manycats";
-    private static final StyleIdsStack LIST_STYLES =
-            StyleIdsStack.newBuilder().addStyleIds(LIST_STYLE_ID).build();
-
-    private static final Element DEFAULT_ELEMENT =
-            Element.newBuilder().setElementStack(ElementStack.getDefaultInstance()).build();
-    private static final Content DEFAULT_CONTENT =
-            Content.newBuilder().setElement(DEFAULT_ELEMENT).build();
-    private static final Element IMAGE_ELEMENT =
-            Element.newBuilder().setImageElement(ImageElement.getDefaultInstance()).build();
-    private static final ElementList DEFAULT_LIST =
-            ElementList.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-    private static final ElementBindingRef ELEMENT_BINDING_REF =
-            ElementBindingRef.newBuilder().setBindingId("shopping").build();
-    private static final Element LIST_WITH_BOUND_LIST = asElement(
-            ElementList.newBuilder()
-                    .addContents(Content.newBuilder().setBoundElement(ELEMENT_BINDING_REF))
-                    .build());
-
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private StyleProvider mStyleProvider;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private Context mContext;
-    private AdapterParameters mAdapterParameters;
-
-    private ElementListAdapter mAdapter;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mFrameContext.makeStyleFor(LIST_STYLES)).thenReturn(mStyleProvider);
-        when(mStyleProvider.getPadding()).thenReturn(EdgeWidths.getDefaultInstance());
-        when(mFrameContext.getActionHandler()).thenReturn(mActionHandler);
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mAssetProvider.isRtL()).thenReturn(false);
-        when(mStyleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
-
-        mAdapterParameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mHostProviders, new FakeClock(), false, false);
-
-        when(mFrameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .thenReturn(mAdapterParameters.mDefaultStyleProvider);
-
-        mAdapter = new KeySupplier().getAdapter(mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testViewDoesNotClip() {
-        assertThat(mAdapter.getBaseView().getClipToPadding()).isFalse();
-    }
-
-    @Test
-    public void testOnCreateAdapter_makesList() {
-        ElementList listWithStyles = ElementList.newBuilder()
-                                             .addContents(DEFAULT_CONTENT)
-                                             .addContents(DEFAULT_CONTENT)
-                                             .addContents(DEFAULT_CONTENT)
-                                             .build();
-
-        mAdapter.createAdapter(asElementWithDefaultStyle(listWithStyles), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(3);
-        assertThat(mAdapter.getBaseView().getOrientation()).isEqualTo(LinearLayout.VERTICAL);
-    }
-
-    @Test
-    public void testOnCreateAdapter_setsStyles() {
-        ElementList listWithStyles = ElementList.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        mAdapter.createAdapter(asElementWithDefaultStyle(listWithStyles), mFrameContext);
-
-        verify(mStyleProvider).applyElementStyles(mAdapter);
-    }
-
-    @Config(shadows = {ExtendedShadowLinearLayout.class})
-    @Test
-    public void testTriggerActions_triggersChildren() {
-        Frame frame = Frame.newBuilder().setTag("Frame").build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        Element baseElement =
-                Element.newBuilder()
-                        .setElementList(ElementList.newBuilder().addContents(
-                                Content.newBuilder().setElement(Element.newBuilder().setElementList(
-                                        ElementList.getDefaultInstance()))))
-                        .build();
-        mAdapter.createAdapter(baseElement, mFrameContext);
-        mAdapter.bindModel(baseElement, mFrameContext);
-
-        // Replace the child adapter so we can verify on it
-        ElementAdapter<?, ?> mockChildAdapter = mock(ElementAdapter.class);
-        mAdapter.mChildAdapters.set(0, mockChildAdapter);
-
-        ExtendedShadowLinearLayout shadowView = Shadow.extract(mAdapter.getView());
-        mAdapter.getView().setVisibility(View.VISIBLE);
-        shadowView.setAttachedToWindow(true);
-
-        View viewport = new View(mContext);
-
-        mAdapter.triggerViewActions(viewport, mFrameContext);
-
-        verify(mockChildAdapter).triggerViewActions(viewport, mFrameContext);
-    }
-
-    @Test
-    public void testOnBindModel_setsStylesOnlyIfBindingIsDefined() {
-        ElementList list = ElementList.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        mAdapter.createAdapter(asElement(list, LIST_STYLES), mFrameContext);
-        verify(mFrameContext).makeStyleFor(LIST_STYLES);
-
-        // Binding an element with a different style will not apply the new style
-        StyleIdsStack otherStyles = StyleIdsStack.newBuilder().addStyleIds("bobcat").build();
-
-        mAdapter.bindModel(asElement(list, otherStyles), mFrameContext);
-        verify(mFrameContext, never()).makeStyleFor(otherStyles);
-
-        // But binding an element with a style binding will re-apply the style
-        StyleIdsStack otherStylesWithBinding =
-                StyleIdsStack.newBuilder()
-                        .addStyleIds("bobcat")
-                        .setStyleBinding(StyleBindingRef.newBuilder().setBindingId("lynx"))
-                        .build();
-        when(mFrameContext.makeStyleFor(otherStylesWithBinding)).thenReturn(mStyleProvider);
-
-        mAdapter.bindModel(asElement(list, otherStylesWithBinding), mFrameContext);
-        verify(mFrameContext).makeStyleFor(otherStylesWithBinding);
-    }
-
-    @Test
-    public void testOnBindModel_failsWithIncompatibleModel() {
-        ElementList listWithThreeElements = ElementList.newBuilder()
-                                                    .addContents(DEFAULT_CONTENT)
-                                                    .addContents(DEFAULT_CONTENT)
-                                                    .addContents(DEFAULT_CONTENT)
-                                                    .build();
-
-        mAdapter.createAdapter(asElementWithDefaultStyle(listWithThreeElements), mFrameContext);
-        mAdapter.bindModel(asElementWithDefaultStyle(listWithThreeElements), mFrameContext);
-        mAdapter.unbindModel();
-
-        ElementList listWithTwoElements = ElementList.newBuilder()
-                                                  .addContents(DEFAULT_CONTENT)
-                                                  .addContents(DEFAULT_CONTENT)
-                                                  .build();
-
-        assertThatRunnable(
-                ()
-                        -> mAdapter.bindModel(
-                                asElementWithDefaultStyle(listWithTwoElements), mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Internal error in adapters per content");
-    }
-
-    @Test
-    public void testOnBindModel_elementListBindingRecreatesAdapter() {
-        Element listWithOneItem =
-                Element.newBuilder()
-                        .setElementList(ElementList.newBuilder().addContents(DEFAULT_CONTENT))
-                        .build();
-        Element listWithTwoItems = Element.newBuilder()
-                                           .setElementList(ElementList.newBuilder()
-                                                                   .addContents(DEFAULT_CONTENT)
-                                                                   .addContents(DEFAULT_CONTENT))
-                                           .build();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setElement(listWithOneItem).build());
-        mAdapter.createAdapter(LIST_WITH_BOUND_LIST, mFrameContext);
-        // No child adapters have been created yet.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setElement(listWithTwoItems).build());
-        mAdapter.bindModel(LIST_WITH_BOUND_LIST, mFrameContext);
-        // The list adapter creates its one view on bind.
-        assertThat(((LinearLayout) mAdapter.getBaseView().getChildAt(0)).getChildCount())
-                .isEqualTo(2);
-
-        mAdapter.unbindModel();
-        // The list adapter has been released.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setElement(listWithOneItem).build());
-        mAdapter.bindModel(LIST_WITH_BOUND_LIST, mFrameContext);
-        // The list adapter can bind to a different model.
-        assertThat(((LinearLayout) mAdapter.getBaseView().getChildAt(0)).getChildCount())
-                .isEqualTo(1);
-    }
-
-    @Test
-    public void testOnBindModel_bindingWithVisibilityGone() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(ELEMENT_BINDING_REF.getBindingId())
-                                    .setElement(Element.newBuilder().setElementList(DEFAULT_LIST))
-                                    .build());
-        mAdapter.createAdapter(LIST_WITH_BOUND_LIST, mFrameContext);
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(ELEMENT_BINDING_REF.getBindingId())
-                                    .setVisibility(Visibility.GONE)
-                                    .build());
-
-        mAdapter.bindModel(LIST_WITH_BOUND_LIST, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_bindingWithNoContent() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(ELEMENT_BINDING_REF.getBindingId())
-                                    .setElement(DEFAULT_ELEMENT)
-                                    .build());
-        mAdapter.createAdapter(LIST_WITH_BOUND_LIST, mFrameContext);
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(ELEMENT_BINDING_REF.getBindingId())
-                                    .build());
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_bindingWithOptionalAbsent() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(ELEMENT_BINDING_REF.getBindingId())
-                                    .setElement(DEFAULT_ELEMENT)
-                                    .build());
-        mAdapter.createAdapter(LIST_WITH_BOUND_LIST, mFrameContext);
-
-        ElementBindingRef optionalBinding =
-                ELEMENT_BINDING_REF.toBuilder().setIsOptional(true).build();
-        ElementList optionalBindingList =
-                ElementList.newBuilder()
-                        .addContents(Content.newBuilder().setBoundElement(optionalBinding))
-                        .build();
-        when(mFrameContext.getElementBindingValue(optionalBinding))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(optionalBinding.getBindingId())
-                                    .build());
-
-        mAdapter.bindModel(asElement(optionalBindingList), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnChild() {
-        ElementList list =
-                ElementList.newBuilder()
-                        .addContents(Content.newBuilder().setElement(
-                                DEFAULT_ELEMENT.toBuilder().setStyleReferences(LIST_STYLES)))
-                        .build();
-        when(mStyleProvider.getGravityHorizontal(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-
-        mAdapter.createAdapter(asElement(list), mFrameContext);
-
-        mAdapter.mChildAdapters.get(0).mWidthPx = 123;
-        mAdapter.mChildAdapters.get(0).mHeightPx = 456;
-
-        mAdapter.bindModel(asElement(list), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).gravity)
-                .isEqualTo(Gravity.CENTER_HORIZONTAL);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).width)
-                .isEqualTo(123);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).height)
-                .isEqualTo(456);
-    }
-
-    @Test
-    public void testOnBindModel_setsMargins() {
-        String marginsStyleId = "spacecat";
-        StyleIdsStack marginsStyles =
-                StyleIdsStack.newBuilder().addStyleIds(marginsStyleId).build();
-        Element elementWithMargins = Element.newBuilder()
-                                             .setStyleReferences(marginsStyles)
-                                             .setElementStack(ElementStack.getDefaultInstance())
-                                             .build();
-        ElementList list = ElementList.newBuilder()
-                                   .addContents(Content.newBuilder().setElement(elementWithMargins))
-                                   .build();
-        when(mFrameContext.makeStyleFor(marginsStyles)).thenReturn(mStyleProvider);
-
-        mAdapter.createAdapter(asElement(list), mFrameContext);
-        mAdapter.bindModel(asElement(list), mFrameContext);
-
-        // Assert that applyMargins is called on the child's layout params
-        ArgumentCaptor<MarginLayoutParams> capturedLayoutParams =
-                ArgumentCaptor.forClass(MarginLayoutParams.class);
-        verify(mStyleProvider).applyMargins(eq(mContext), capturedLayoutParams.capture());
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(MarginLayoutParams.class);
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isSameInstanceAs(capturedLayoutParams.getValue());
-    }
-
-    @Test
-    public void testReleaseAdapter() {
-        ElementList listWithStyles = ElementList.newBuilder()
-                                             .addContents(DEFAULT_CONTENT)
-                                             .addContents(DEFAULT_CONTENT)
-                                             .addContents(DEFAULT_CONTENT)
-                                             .build();
-
-        mAdapter.createAdapter(asElementWithDefaultStyle(listWithStyles), mFrameContext);
-        mAdapter.bindModel(asElementWithDefaultStyle(listWithStyles), mFrameContext);
-
-        mAdapter.releaseAdapter();
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testGetVerticalGravity_noModel() {
-        assertThat(mAdapter.getVerticalGravity(Gravity.CLIP_VERTICAL))
-                .isEqualTo(Gravity.CLIP_VERTICAL);
-    }
-
-    @Test
-    public void testGetStyleIdsStack() {
-        ElementList listWithStyles = ElementList.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        mAdapter.createAdapter(asElement(listWithStyles, LIST_STYLES), mFrameContext);
-
-        assertThat(mAdapter.getElementStyleIdsStack()).isEqualTo(LIST_STYLES);
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        ElementList model = ElementList.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        Element elementWithModel = Element.newBuilder().setElementList(model).build();
-        assertThat(mAdapter.getModelFromElement(elementWithModel)).isSameInstanceAs(model);
-
-        Element elementWithWrongModel =
-                Element.newBuilder().setCustomElement(CustomElement.getDefaultInstance()).build();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(elementWithWrongModel))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing ElementList");
-
-        Element emptyElement = Element.getDefaultInstance();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(emptyElement))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing ElementList");
-    }
-
-    @Test
-    public void testSetLayoutParamsOnChild() {
-        ElementList list =
-                ElementList.newBuilder()
-                        .addContents(Content.newBuilder().setElement(
-                                DEFAULT_ELEMENT.toBuilder().setStyleReferences(LIST_STYLES)))
-                        .build();
-        when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(123);
-        when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(456);
-        when(mStyleProvider.getGravityHorizontal(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-
-        mAdapter.createAdapter(asElement(list), mFrameContext);
-
-        LayoutParams layoutParams = new LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        mAdapter.setLayoutParams(layoutParams);
-
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).gravity)
-                .isEqualTo(Gravity.CENTER_HORIZONTAL);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).width)
-                .isEqualTo(123);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).height)
-                .isEqualTo(456);
-    }
-
-    @Test
-    public void testSetLayoutParams_childWidthSet() {
-        int childWidth = 5;
-
-        ElementList list = ElementList.newBuilder()
-                                   .addContents(Content.newBuilder().setElement(IMAGE_ELEMENT))
-                                   .build();
-
-        mAdapter.createAdapter(asElement(list), mFrameContext);
-        StyleProvider childStyleProvider = mock(StyleProvider.class);
-        ImageElementAdapter mockChildAdapter = mock(ImageElementAdapter.class);
-        when(mockChildAdapter.getComputedWidthPx()).thenReturn(childWidth);
-        when(mockChildAdapter.getElementStyle()).thenReturn(childStyleProvider);
-        when(mockChildAdapter.getHorizontalGravity(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mockChildAdapter.getElementStyle().hasGravityHorizontal()).thenReturn(true);
-        mAdapter.mChildAdapters.set(0, mockChildAdapter);
-
-        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        mAdapter.setLayoutParams(params);
-
-        ArgumentCaptor<LinearLayout.LayoutParams> childLayoutParamsCaptor =
-                ArgumentCaptor.forClass(LinearLayout.LayoutParams.class);
-        verify(mockChildAdapter).setLayoutParams(childLayoutParamsCaptor.capture());
-
-        LinearLayout.LayoutParams childLayoutParams = childLayoutParamsCaptor.getValue();
-        assertThat(childLayoutParams.width).isEqualTo(childWidth);
-        assertThat(childLayoutParams.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
-
-        verify(childStyleProvider).applyMargins(mContext, childLayoutParams);
-    }
-
-    @Test
-    public void testSetLayoutParams_widthSetOnList() {
-        ElementList list = ElementList.newBuilder()
-                                   .addContents(Content.newBuilder().setElement(IMAGE_ELEMENT))
-                                   .build();
-
-        mAdapter.createAdapter(asElement(list), mFrameContext);
-
-        LinearLayout.LayoutParams params =
-                new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
-        params.weight = 1;
-        mAdapter.setLayoutParams(params);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).width)
-                .isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
-        assertThat(((LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams()).height)
-                .isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT);
-    }
-
-    private static Element asElement(ElementList elementList) {
-        return Element.newBuilder().setElementList(elementList).build();
-    }
-
-    private static Element asElement(ElementList elementList, StyleIdsStack styles) {
-        return Element.newBuilder().setStyleReferences(styles).setElementList(elementList).build();
-    }
-
-    private static Element asElementWithDefaultStyle(ElementList elementList) {
-        return Element.newBuilder()
-                .setStyleReferences(LIST_STYLES)
-                .setElementList(elementList)
-                .build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementStackAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementStackAdapterTest.java
deleted file mode 100644
index be4180a..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementStackAdapterTest.java
+++ /dev/null
@@ -1,222 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static java.util.Arrays.asList;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.ImageView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ElementBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementStack;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Tests of the {@link ElementStackAdapter} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ElementStackAdapterTest {
-    private static final Content INLINE_CONTENT =
-            Content.newBuilder()
-                    .setElement(
-                            Element.newBuilder().setElementList(ElementList.getDefaultInstance()))
-                    .build();
-    private static final Element BOUND_ELEMENT =
-            Element.newBuilder().setGridRow(GridRow.getDefaultInstance()).build();
-    private static final String ELEMENT_BINDING_ID = "RowBinding";
-    private static final ElementBindingRef ELEMENT_BINDING =
-            ElementBindingRef.newBuilder().setBindingId(ELEMENT_BINDING_ID).build();
-    private static final BindingValue ELEMENT_BINDING_VALUE =
-            BindingValue.newBuilder()
-                    .setBindingId(ELEMENT_BINDING_ID)
-                    .setElement(BOUND_ELEMENT)
-                    .build();
-    private static final Content BOUND_CONTENT =
-            Content.newBuilder().setBoundElement(ELEMENT_BINDING).build();
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private AssetProvider mAssetProvider;
-    @Mock
-    private StyleProvider mMockStyleProvider;
-    @Mock
-    private HostProviders mMockHostProviders;
-
-    private Context mContext;
-    private AdapterParameters mAdapterParameters;
-
-    private ElementStackAdapter mAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(ELEMENT_BINDING_VALUE);
-        when(mFrameContext.makeStyleFor(any())).thenReturn(mMockStyleProvider);
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
-                .thenAnswer(invocation -> invocation.getArguments()[0]);
-        when(mMockHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mMockStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-
-        mAdapterParameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mMockHostProviders, new FakeClock(), false, false);
-
-        mAdapter = new ElementStackAdapter.KeySupplier().getAdapter(mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testCreatesInlineAdaptersInOnCreate() {
-        Element element = asElement(asList(INLINE_CONTENT, BOUND_CONTENT));
-
-        mAdapter.createAdapter(element, mFrameContext);
-
-        assertThat(mAdapter.mChildAdapters).hasSize(1);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(ElementListAdapter.class);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-    }
-
-    @Test
-    public void testCreatesBoundAdaptersInOnBind() {
-        Element element = asElement(asList(INLINE_CONTENT, BOUND_CONTENT));
-
-        mAdapter.createAdapter(element, mFrameContext);
-        mAdapter.bindModel(element, mFrameContext);
-
-        assertThat(mAdapter.mChildAdapters).hasSize(2);
-        assertThat(mAdapter.mChildAdapters.get(0)).isInstanceOf(ElementListAdapter.class);
-        assertThat(mAdapter.mChildAdapters.get(1)).isInstanceOf(GridRowAdapter.class);
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(2);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-        assertThat(mAdapter.getBaseView().getChildAt(1))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(1).getView());
-    }
-
-    @Test
-    public void testSetsLayoutParamsOnChildForInlineAdapters() {
-        Content imageContent =
-                Content.newBuilder()
-                        .setElement(Element.newBuilder().setImageElement(
-                                ImageElement.newBuilder().setImage(Image.getDefaultInstance())))
-                        .build();
-        Element element = asElement(Collections.singletonList(imageContent));
-
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(123);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(456);
-        when(mMockStyleProvider.getScaleType()).thenReturn(ImageView.ScaleType.CENTER_CROP);
-
-        mAdapter.createAdapter(element, mFrameContext);
-        mAdapter.bindModel(element, mFrameContext);
-
-        LayoutParams inlineAdapterLayoutParams =
-                mAdapter.mChildAdapters.get(0).getView().getLayoutParams();
-
-        assertThat(inlineAdapterLayoutParams.width).isEqualTo(123);
-        assertThat(inlineAdapterLayoutParams.height).isEqualTo(456);
-    }
-
-    @Test
-    public void testSetsLayoutParamsOnChildForBoundAdapters() {
-        ElementBindingRef imageBinding =
-                ElementBindingRef.newBuilder().setBindingId("image").build();
-        BindingValue imageBindingValue =
-                BindingValue.newBuilder()
-                        .setElement(Element.newBuilder().setImageElement(
-                                ImageElement.newBuilder().setImage(Image.getDefaultInstance())))
-                        .build();
-        when(mFrameContext.getElementBindingValue(imageBinding)).thenReturn(imageBindingValue);
-        Element element = asElement(Collections.singletonList(
-                Content.newBuilder().setBoundElement(imageBinding).build()));
-
-        when(mMockStyleProvider.hasWidth()).thenReturn(true);
-        when(mMockStyleProvider.hasHeight()).thenReturn(true);
-        when(mMockStyleProvider.getWidthSpecPx(mContext)).thenReturn(123);
-        when(mMockStyleProvider.getHeightSpecPx(mContext)).thenReturn(456);
-        when(mMockStyleProvider.getScaleType()).thenReturn(ImageView.ScaleType.CENTER_CROP);
-
-        mAdapter.createAdapter(element, mFrameContext);
-        mAdapter.bindModel(element, mFrameContext);
-
-        LayoutParams boundAdapterLayoutParams =
-                mAdapter.mChildAdapters.get(0).getView().getLayoutParams();
-
-        assertThat(boundAdapterLayoutParams.width).isEqualTo(123);
-        assertThat(boundAdapterLayoutParams.height).isEqualTo(456);
-    }
-
-    @Test
-    public void testSetsMarginsOnChild() {
-        Element element = asElement(Collections.singletonList(INLINE_CONTENT));
-
-        mAdapter.createAdapter(element, mFrameContext);
-        mAdapter.bindModel(element, mFrameContext);
-
-        MarginLayoutParams childAdapterLayoutParams =
-                (MarginLayoutParams) mAdapter.mChildAdapters.get(0).getView().getLayoutParams();
-
-        verify(mMockStyleProvider).applyMargins(mContext, childAdapterLayoutParams);
-    }
-
-    @Test
-    public void testGetContentsFromModel() {
-        ElementStack model = ElementStack.newBuilder()
-                                     .addAllContents(asList(INLINE_CONTENT, BOUND_CONTENT))
-                                     .build();
-
-        assertThat(mAdapter.getContentsFromModel(model)).isSameInstanceAs(model.getContentsList());
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        Element element = asElement(asList(INLINE_CONTENT, BOUND_CONTENT));
-
-        assertThat(mAdapter.getModelFromElement(element))
-                .isSameInstanceAs(element.getElementStack());
-    }
-
-    private Element asElement(List<Content> contents) {
-        return Element.newBuilder()
-                .setElementStack(ElementStack.newBuilder().addAllContents(contents))
-                .build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameAdapterImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameAdapterImplTest.java
deleted file mode 100644
index 3500da2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameAdapterImplTest.java
+++ /dev/null
@@ -1,853 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.drawable.ColorDrawable;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.TemplateBinder.TemplateAdapterModel;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.EventLogger;
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.LogDataCallback;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Actions;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.VisibilityAction;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ActionsBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingContext;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementStack;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TemplateInvocation;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.Fill;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietAndroidSupport.ShardingControl;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheets;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.ShadowsProto.ElevationShadow;
-import org.chromium.components.feed.core.proto.ui.piet.ShadowsProto.Shadow;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.GravityHorizontal;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.GravityVertical;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests of the {@link FrameAdapterImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FrameAdapterImplTest {
-    private static final Content DEFAULT_CONTENT =
-            Content.newBuilder()
-                    .setElement(Element.newBuilder().setElementList(
-                            ElementList.newBuilder().addContents(
-                                    Content.newBuilder().setElement(Element.getDefaultInstance()))))
-                    .build();
-    private static final int FRAME_WIDTH = 321;
-    private final LogData mLogDataTest = LogData.getDefaultInstance();
-
-    @Mock
-    private AssetProvider mAssetProvider;
-    @Mock
-    private ElementAdapterFactory mAdapterFactory;
-    @Mock
-    private TemplateBinder mTemplateBinder;
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private DebugBehavior mDebugBehavior;
-    @Mock
-    private DebugLogger mDebugLogger;
-    @Mock
-    private StyleProvider mStyleProvider;
-    @Mock
-    private ElementAdapter<? extends View, ?> mElementAdapter;
-    @Mock
-    private ElementAdapter<? extends View, ?> mTemplateAdapter;
-    @Mock
-    private CustomElementProvider mCustomElementProvider;
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private EventLogger mEventLogger;
-    @Mock
-    private PietStylesHelperFactory mStylesHelpers;
-    @Mock
-    private RoundedCornerMaskCache mMaskCache;
-
-    @Captor
-    private ArgumentCaptor<LayoutParams> mLayoutParamsCaptor;
-
-    private Context mContext;
-    private List<PietSharedState> mPietSharedStates;
-    private AdapterParameters mAdapterParameters;
-    private boolean mCallbackBound;
-    private boolean mCallbackUnbound;
-    private FrameAdapterImpl mFrameAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        LogDataCallback logDataCallback = new LogDataCallback() {
-            @Override
-            public void onBind(LogData logData, View view) {
-                assertThat(mLogDataTest).isEqualTo(logData);
-                mCallbackBound = true;
-            }
-
-            @Override
-            public void onUnbind(LogData logData, View view) {
-                assertThat(mLogDataTest).isEqualTo(logData);
-                mCallbackUnbound = true;
-            }
-        };
-        mAdapterParameters = new AdapterParameters(null, Suppliers.of(new FrameLayout(mContext)),
-                mHostProviders, new ParameterizedTextEvaluator(new FakeClock()), mAdapterFactory,
-                mTemplateBinder, new FakeClock(), mStylesHelpers, mMaskCache, false, false);
-        when(mElementAdapter.getView()).thenReturn(new LinearLayout(mContext));
-        when(mTemplateAdapter.getView()).thenReturn(new LinearLayout(mContext));
-        doReturn(mElementAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(any(Element.class), eq(mFrameContext));
-        doReturn(mTemplateAdapter)
-                .when(mTemplateBinder)
-                .createAndBindTemplateAdapter(any(TemplateAdapterModel.class), eq(mFrameContext));
-        when(mElementAdapter.getHorizontalGravity(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mElementAdapter.getVerticalGravity(anyInt())).thenReturn(Gravity.BOTTOM);
-        when(mTemplateAdapter.getHorizontalGravity(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mTemplateAdapter.getVerticalGravity(anyInt())).thenReturn(Gravity.BOTTOM);
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mHostProviders.getLogDataCallback()).thenReturn(logDataCallback);
-        when(mAssetProvider.isRtLSupplier()).thenReturn(Suppliers.of(false));
-        when(mFrameContext.reportMessage(eq(MessageType.ERROR), anyString()))
-                .thenAnswer(invocationOnMock -> {
-                    throw new RuntimeException((String) invocationOnMock.getArguments()[1]);
-                });
-        when(mFrameContext.getDebugLogger()).thenReturn(mDebugLogger);
-        when(mFrameContext.getDebugBehavior()).thenReturn(DebugBehavior.VERBOSE);
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(mStyleProvider);
-        when(mFrameContext.getFrame()).thenReturn(Frame.getDefaultInstance());
-        when(mElementAdapter.getElementStyle()).thenReturn(mStyleProvider);
-        when(mTemplateAdapter.getElementStyle()).thenReturn(mStyleProvider);
-
-        mPietSharedStates = new ArrayList<>();
-        mFrameAdapter = new FrameAdapterImpl(
-                mContext, mAdapterParameters, mActionHandler, mEventLogger, mDebugBehavior) {
-            @Override
-            FrameContext createFrameContext(Frame frame, int frameWidthPx,
-                    List<PietSharedState> pietSharedStates, View view) {
-                return mFrameContext;
-            }
-        };
-    }
-
-    @Test
-    public void testCreate() {
-        LinearLayout linearLayout = mFrameAdapter.getFrameContainer();
-        assertThat(linearLayout).isNotNull();
-        LayoutParams layoutParams = linearLayout.getLayoutParams();
-        assertThat(layoutParams).isNotNull();
-        assertThat(layoutParams.width).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(layoutParams.height).isEqualTo(LayoutParams.WRAP_CONTENT);
-    }
-
-    @Test
-    public void testGetBoundAdaptersForContent() {
-        Content content = DEFAULT_CONTENT;
-
-        String templateId = "loaf";
-        when(mFrameContext.getTemplate(templateId)).thenReturn(Template.getDefaultInstance());
-        List<ElementAdapter<?, ?>> viewAdapters =
-                mFrameAdapter.getBoundAdaptersForContent(content, mFrameContext);
-        assertThat(viewAdapters).containsExactly(mElementAdapter);
-
-        content = Content.newBuilder()
-                          .setTemplateInvocation(
-                                  TemplateInvocation.newBuilder().setTemplateId(templateId))
-                          .build();
-        viewAdapters = mFrameAdapter.getBoundAdaptersForContent(content, mFrameContext);
-        assertThat(viewAdapters).isEmpty();
-
-        content = Content.newBuilder()
-                          .setTemplateInvocation(
-                                  TemplateInvocation.newBuilder()
-                                          .setTemplateId(templateId)
-                                          .addBindingContexts(BindingContext.getDefaultInstance()))
-                          .build();
-        viewAdapters = mFrameAdapter.getBoundAdaptersForContent(content, mFrameContext);
-        assertThat(viewAdapters).containsExactly(mTemplateAdapter);
-
-        content = Content.newBuilder()
-                          .setTemplateInvocation(
-                                  TemplateInvocation.newBuilder()
-                                          .setTemplateId(templateId)
-                                          .addBindingContexts(
-                                                  BindingContext.newBuilder().addBindingValues(
-                                                          BindingValue.newBuilder().setBindingId(
-                                                                  "1")))
-                                          .addBindingContexts(
-                                                  BindingContext.newBuilder().addBindingValues(
-                                                          BindingValue.newBuilder().setBindingId(
-                                                                  "2")))
-                                          .addBindingContexts(
-                                                  BindingContext.newBuilder().addBindingValues(
-                                                          BindingValue.newBuilder().setBindingId(
-                                                                  "3"))))
-                          .build();
-        viewAdapters = mFrameAdapter.getBoundAdaptersForContent(content, mFrameContext);
-        assertThat(viewAdapters)
-                .containsExactly(mTemplateAdapter, mTemplateAdapter, mTemplateAdapter);
-        verify(mTemplateBinder)
-                .createAndBindTemplateAdapter(
-                        new TemplateAdapterModel(Template.getDefaultInstance(),
-                                content.getTemplateInvocation().getBindingContexts(0)),
-                        mFrameContext);
-        verify(mTemplateBinder)
-                .createAndBindTemplateAdapter(
-                        new TemplateAdapterModel(Template.getDefaultInstance(),
-                                content.getTemplateInvocation().getBindingContexts(1)),
-                        mFrameContext);
-        verify(mTemplateBinder)
-                .createAndBindTemplateAdapter(
-                        new TemplateAdapterModel(Template.getDefaultInstance(),
-                                content.getTemplateInvocation().getBindingContexts(2)),
-                        mFrameContext);
-
-        verify(mFrameContext, never()).reportMessage(anyInt(), anyString());
-    }
-
-    /**
-     * This test sets up all real objects to ensure that real adapters are bound and unbound, etc.
-     */
-    @Test
-    public void testBindAndUnbind_respectsAdapterLifecycle() {
-        String templateId = "template";
-        ElementList defaultList =
-                ElementList.newBuilder()
-                        .addContents(
-                                Content.newBuilder().setElement(Element.newBuilder().setElementList(
-                                        ElementList.getDefaultInstance())))
-                        .addContents(Content.newBuilder().setElement(
-                                Element.newBuilder().setGridRow(GridRow.getDefaultInstance())))
-                        .build();
-        AdapterParameters adapterParameters =
-                new AdapterParameters(mContext, Suppliers.of(new LinearLayout(mContext)),
-                        mHostProviders, new FakeClock(), false, false);
-        PietSharedState pietSharedState =
-                PietSharedState.newBuilder()
-                        .addTemplates(Template.newBuilder()
-                                              .setTemplateId(templateId)
-                                              .setElement(Element.newBuilder().setElementList(
-                                                      defaultList)))
-                        .build();
-        mPietSharedStates.add(pietSharedState);
-        MediaQueryHelper mediaQueryHelper = new MediaQueryHelper(
-                FRAME_WIDTH, adapterParameters.mHostProviders.getAssetProvider(), mContext);
-        PietStylesHelper stylesHelper =
-                adapterParameters.mPietStylesHelperFactory.get(mPietSharedStates, mediaQueryHelper);
-        FrameContext frameContext = FrameContext.createFrameContext(
-                Frame.newBuilder().setStylesheets(Stylesheets.getDefaultInstance()).build(),
-                mPietSharedStates, stylesHelper, mDebugBehavior, new DebugLogger(), mActionHandler,
-                new HostProviders(
-                        mAssetProvider, mCustomElementProvider, new HostBindingProvider(), null),
-                new FrameLayout(mContext));
-
-        mFrameAdapter = new FrameAdapterImpl(
-                mContext, adapterParameters, mActionHandler, mEventLogger, mDebugBehavior);
-
-        Content content = Content.newBuilder()
-                                  .setElement(Element.newBuilder().setElementList(defaultList))
-                                  .build();
-
-        List<ElementAdapter<?, ?>> viewAdapters =
-                mFrameAdapter.getBoundAdaptersForContent(content, frameContext);
-        assertThat(viewAdapters).hasSize(1);
-        ElementAdapter<?, ?> viewAdapter = viewAdapters.get(0);
-        assertThat(viewAdapter.getModel()).isEqualTo(content.getElement().getElementList());
-        mFrameAdapter.bindModel(Frame.newBuilder().addContents(content).build(), FRAME_WIDTH, null,
-                mPietSharedStates);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-        mFrameAdapter.unbindModel();
-
-        content = Content.newBuilder()
-                          .setTemplateInvocation(
-                                  TemplateInvocation.newBuilder()
-                                          .setTemplateId(templateId)
-                                          .addBindingContexts(BindingContext.getDefaultInstance()))
-                          .build();
-        viewAdapters = mFrameAdapter.getBoundAdaptersForContent(content, frameContext);
-        assertThat(viewAdapters).hasSize(1);
-        viewAdapter = viewAdapters.get(0);
-        assertThat(viewAdapter.getModel()).isEqualTo(defaultList);
-        mFrameAdapter.bindModel(Frame.newBuilder().addContents(content).build(), FRAME_WIDTH, null,
-                mPietSharedStates);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-        mFrameAdapter.unbindModel();
-    }
-
-    /**
-     * This test sets up all real objects to ensure that real adapters are bound and unbound, etc.
-     */
-    @Test
-    public void testBindAndUnbind_resetsStylesWhenWrapperViewIsAdded() {
-        AdapterParameters adapterParameters =
-                new AdapterParameters(mContext, Suppliers.of(new LinearLayout(mContext)),
-                        mHostProviders, new FakeClock(), false, false);
-        PietSharedState pietSharedState =
-                PietSharedState.newBuilder()
-                        .addStylesheets(
-                                Stylesheet.newBuilder()
-                                        .setStylesheetId("stylesheet")
-                                        // clang-format off
-                            .addStyles(
-                                    Style.newBuilder()
-                                            .setStyleId("style1")
-                                            .setBackground(Fill.newBuilder().setColor(
-                                                    0x11111111))
-                                            .setPadding(EdgeWidths.newBuilder()
-                                                                .setStart(1)
-                                                                .setTop(1))
-                                            .setMargins(EdgeWidths.newBuilder()
-                                                                .setStart(1)
-                                                                .setTop(1))
-                                            .setColor(0x11111111)
-                                            .setHeight(1)
-                                            .setWidth(1)
-                                            .setShadow(
-                                                    Shadow.newBuilder()
-                                                            .setElevationShadow(
-                                                                    ElevationShadow
-                                                                            .newBuilder()
-                                                                            .setElevation(
-                                                                                    1)))
-                                            .setOpacity(0.5f)
-                                            .setGravityHorizontal(
-                                                    GravityHorizontal.GRAVITY_START)
-                                            .setGravityVertical(
-                                                    GravityVertical.GRAVITY_BOTTOM))
-                            .addStyles(
-                                    Style.newBuilder()
-                                            .setStyleId("style2")
-                                            .setBackground(Fill.newBuilder().setColor(
-                                                    0x22222222))
-                                            .setPadding(EdgeWidths.newBuilder()
-                                                                .setEnd(2)
-                                                                .setTop(2))
-                                            .setMargins(EdgeWidths.newBuilder()
-                                                                .setEnd(2)
-                                                                .setTop(2))
-                                            .setColor(0x222222)
-                                            .setHeight(2)
-                                            .setWidth(2)
-                                            .setShadow(
-                                                    Shadow.newBuilder()
-                                                            .setElevationShadow(
-                                                                    ElevationShadow
-                                                                            .newBuilder()
-                                                                            .setElevation(
-                                                                                    2)))
-                                            .setOpacity(0.25f)
-                                            // Rounded corners will introduce an
-                                            // overlay.
-                                            .setRoundedCorners(
-                                                    RoundedCorners.newBuilder()
-                                                            .setRadiusDp(2)
-                                                            .setBitmask(2))
-                                            .setGravityHorizontal(
-                                                    GravityHorizontal.GRAVITY_END)))
-                        // clang-format on
-                        .build();
-        mPietSharedStates.add(pietSharedState);
-
-        mFrameAdapter = new FrameAdapterImpl(
-                mContext, adapterParameters, mActionHandler, mEventLogger, mDebugBehavior);
-
-        ImageElement defaultImage =
-                ImageElement.newBuilder().setImage(Image.getDefaultInstance()).build();
-        Content content1 =
-                Content.newBuilder()
-                        .setElement(Element.newBuilder().setElementList(
-                                ElementList.newBuilder().addContents(
-                                        Content.newBuilder().setElement(
-                                                Element.newBuilder()
-                                                        .setStyleReferences(
-                                                                StyleIdsStack.newBuilder()
-                                                                        .addStyleIds("style1"))
-                                                        .setImageElement(defaultImage)))))
-                        .build();
-        Content content2 =
-                Content.newBuilder()
-                        .setElement(Element.newBuilder().setElementList(
-                                ElementList.newBuilder().addContents(
-                                        Content.newBuilder().setElement(
-                                                Element.newBuilder()
-                                                        .setStyleReferences(
-                                                                StyleIdsStack.newBuilder()
-                                                                        .addStyleIds("style2"))
-                                                        .setImageElement(defaultImage)))))
-                        .build();
-
-        // Bind to a Frame with no wrapper view
-        mFrameAdapter.bindModel(
-                Frame.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds("stylesheet"))
-                        .addContents(content1)
-                        .build(),
-                0, null, mPietSharedStates);
-
-        ImageView imageView =
-                (ImageView) ((LinearLayout) mFrameAdapter.getView().getChildAt(0)).getChildAt(0);
-        LinearLayout.LayoutParams imageViewParams =
-                (LinearLayout.LayoutParams) imageView.getLayoutParams();
-        assertThat(imageViewParams.leftMargin).isEqualTo(1);
-        assertThat(imageViewParams.topMargin).isEqualTo(1);
-        assertThat(imageViewParams.rightMargin).isEqualTo(0);
-        assertThat(imageViewParams.bottomMargin).isEqualTo(0);
-        assertThat(imageView.getPaddingStart()).isEqualTo(1);
-        assertThat(imageView.getPaddingTop()).isEqualTo(1);
-        assertThat(imageView.getPaddingEnd()).isEqualTo(0);
-        assertThat(imageView.getPaddingBottom()).isEqualTo(0);
-        assertThat(imageViewParams.height).isEqualTo(1);
-        assertThat(imageViewParams.width).isEqualTo(1);
-        assertThat(imageView.getElevation()).isWithin(0.1f).of(1);
-        assertThat(imageView.getAlpha()).isWithin(0.1f).of(0.5f);
-
-        mFrameAdapter.unbindModel();
-
-        // Re-bind to a Frame with a wrapper view
-        // This will recycle the ImageView, but it will be within a wrapper view.
-        // Ensure that the properties on the ImageView get unset correctly.
-        mFrameAdapter.bindModel(
-                Frame.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds("stylesheet"))
-                        .addContents(content2)
-                        .build(),
-                0, null, Collections.singletonList(pietSharedState));
-
-        FrameLayout wrapperView =
-                (FrameLayout) ((LinearLayout) mFrameAdapter.getView().getChildAt(0)).getChildAt(0);
-        LinearLayout.LayoutParams wrapperViewParams =
-                (LinearLayout.LayoutParams) wrapperView.getLayoutParams();
-
-        // Exactly one of the wrapper view and the image view have the specified params set.
-        assertThat(wrapperViewParams.leftMargin).isEqualTo(0);
-        assertThat(wrapperViewParams.topMargin).isEqualTo(2);
-        assertThat(wrapperViewParams.rightMargin).isEqualTo(2);
-        assertThat(wrapperViewParams.bottomMargin).isEqualTo(0);
-        assertThat(wrapperView.getPaddingStart()).isEqualTo(0);
-        assertThat(wrapperView.getPaddingTop()).isEqualTo(0);
-        assertThat(wrapperView.getPaddingEnd()).isEqualTo(0);
-        assertThat(wrapperView.getPaddingBottom()).isEqualTo(0);
-        assertThat(wrapperViewParams.height).isEqualTo(2);
-        assertThat(wrapperViewParams.width).isEqualTo(2);
-        assertThat(wrapperView.getElevation()).isWithin(0.1f).of(2);
-        assertThat(wrapperView.getAlpha()).isWithin(0.1f).of(1.0f);
-
-        assertThat(wrapperView.getChildAt(0)).isSameInstanceAs(imageView);
-        FrameLayout.LayoutParams newImageViewParams =
-                (FrameLayout.LayoutParams) imageView.getLayoutParams();
-        assertThat(newImageViewParams.leftMargin).isEqualTo(0);
-        assertThat(newImageViewParams.topMargin).isEqualTo(0);
-        assertThat(newImageViewParams.rightMargin).isEqualTo(0);
-        assertThat(newImageViewParams.bottomMargin).isEqualTo(0);
-        assertThat(imageView.getPaddingStart()).isEqualTo(0);
-        assertThat(imageView.getPaddingTop()).isEqualTo(2);
-        assertThat(imageView.getPaddingEnd()).isEqualTo(2);
-        assertThat(imageView.getPaddingBottom()).isEqualTo(0);
-        assertThat(newImageViewParams.height).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(newImageViewParams.width).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(imageView.getElevation()).isWithin(0.1f).of(0);
-        assertThat(imageView.getAlpha()).isWithin(0.1f).of(0.25f);
-    }
-
-    @Test
-    public void testBindModel_defaultDimensions() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        when(mElementAdapter.getComputedWidthPx()).thenReturn(StyleProvider.DIMENSION_NOT_SET);
-        when(mElementAdapter.getComputedHeightPx()).thenReturn(StyleProvider.DIMENSION_NOT_SET);
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mFrameAdapter.getView().getChildCount()).isEqualTo(1);
-
-        verify(mElementAdapter).setLayoutParams(mLayoutParamsCaptor.capture());
-        assertThat(mLayoutParamsCaptor.getValue().width).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(mLayoutParamsCaptor.getValue().height).isEqualTo(LayoutParams.WRAP_CONTENT);
-    }
-
-    @Test
-    public void testBindModel_explicitDimensions() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        int width = 123;
-        int height = 456;
-        when(mElementAdapter.getComputedWidthPx()).thenReturn(width);
-        when(mElementAdapter.getComputedHeightPx()).thenReturn(height);
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mFrameAdapter.getView().getChildCount()).isEqualTo(1);
-
-        verify(mElementAdapter).setLayoutParams(mLayoutParamsCaptor.capture());
-        assertThat(mLayoutParamsCaptor.getValue().width).isEqualTo(width);
-        assertThat(mLayoutParamsCaptor.getValue().height).isEqualTo(height);
-    }
-
-    @Test
-    public void testBindModel_gravity() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        when(mElementAdapter.getHorizontalGravity(anyInt())).thenReturn(Gravity.CENTER_HORIZONTAL);
-        when(mElementAdapter.getVerticalGravity(anyInt())).thenReturn(Gravity.BOTTOM);
-        when(mElementAdapter.getGravity(anyInt()))
-                .thenReturn(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        verify(mElementAdapter).setLayoutParams(mLayoutParamsCaptor.capture());
-        assertThat(((LinearLayout.LayoutParams) mLayoutParamsCaptor.getValue()).gravity)
-                .isEqualTo(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-    }
-
-    @Test
-    public void testCreateFrameContext_recyclesPietStylesHelper() {
-        AdapterParameters adapterParameters =
-                new AdapterParameters(mContext, Suppliers.of(new LinearLayout(mContext)),
-                        mHostProviders, new FakeClock(), false, false);
-        PietSharedState pietSharedState = PietSharedState.newBuilder()
-                                                  .addStylesheets(Stylesheet.newBuilder().addStyles(
-                                                          Style.newBuilder().setStyleId("style")))
-                                                  .build();
-        mPietSharedStates.add(pietSharedState);
-        FrameAdapterImpl frameAdapter = new FrameAdapterImpl(
-                mContext, adapterParameters, mActionHandler, mEventLogger, mDebugBehavior);
-
-        FrameContext frameContext1 =
-                frameAdapter.createFrameContext(Frame.newBuilder().setTag("frame1").build(),
-                        FRAME_WIDTH, mPietSharedStates, frameAdapter.getView());
-        FrameContext frameContext2 =
-                frameAdapter.createFrameContext(Frame.newBuilder().setTag("frame2").build(),
-                        FRAME_WIDTH, mPietSharedStates, frameAdapter.getView());
-        FrameContext frameContext3 =
-                frameAdapter.createFrameContext(Frame.newBuilder().setTag("frame3").build(),
-                        FRAME_WIDTH + 1, mPietSharedStates, frameAdapter.getView());
-
-        // These should both pull the same object from the cache.
-        assertThat(frameContext1.mStyleshelper).isSameInstanceAs(frameContext2.mStyleshelper);
-
-        // This one is different because of the different frame width.
-        assertThat(frameContext1.mStyleshelper).isNotEqualTo(frameContext3.mStyleshelper);
-    }
-
-    @Test
-    public void testAdapterWithActions() {
-        String templateId = "lights-camera";
-        String actionBindingId = "action";
-        ActionsBindingRef actionBinding =
-                ActionsBindingRef.newBuilder().setBindingId(actionBindingId).build();
-        Template templateWithActions =
-                Template.newBuilder()
-                        .setTemplateId(templateId)
-                        .setElement(Element.newBuilder()
-                                            .setActionsBinding(actionBinding)
-                                            .setElementList(ElementList.getDefaultInstance()))
-                        .build();
-        BindingValue actionBindingValue = BindingValue.newBuilder()
-                                                  .setBindingId(actionBindingId)
-                                                  .setActions(Actions.newBuilder().setOnClickAction(
-                                                          Action.getDefaultInstance()))
-                                                  .build();
-        Content content =
-                Content.newBuilder()
-                        .setTemplateInvocation(
-                                TemplateInvocation.newBuilder()
-                                        .setTemplateId(templateId)
-                                        .addBindingContexts(
-                                                BindingContext.newBuilder().addBindingValues(
-                                                        actionBindingValue)))
-                        .build();
-        Frame frame = Frame.newBuilder().addContents(content).build();
-        PietSharedState pietSharedState =
-                PietSharedState.newBuilder().addTemplates(templateWithActions).build();
-
-        mPietSharedStates.add(pietSharedState);
-
-        AdapterParameters parameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mHostProviders, new FakeClock(), false, false);
-        FrameAdapterImpl frameAdapter = new FrameAdapterImpl(
-                mContext, parameters, mActionHandler, mEventLogger, mDebugBehavior);
-
-        frameAdapter.bindModel(frame, FRAME_WIDTH, null, mPietSharedStates);
-
-        assertThat(frameAdapter.getFrameContainer().getChildCount()).isEqualTo(1);
-        assertThat(frameAdapter.getFrameContainer().getChildAt(0).hasOnClickListeners()).isTrue();
-    }
-
-    @Test
-    public void testBackgroundColor() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        mStyleProvider.createBackground();
-    }
-
-    @Test
-    public void testUnsetBackgroundIfNotDefined() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-
-        // Set background
-        when(mStyleProvider.createBackground()).thenReturn(new ColorDrawable(12345));
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        assertThat(mFrameAdapter.getView().getBackground()).isNotNull();
-        mFrameAdapter.unbindModel();
-
-        // Re-bind and check that background is unset
-        when(mStyleProvider.createBackground()).thenReturn(null);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        assertThat(mFrameAdapter.getView().getBackground()).isNull();
-    }
-
-    @Test
-    public void testUnsetBackgroundIfCreateBackgroundFails() {
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-
-        // Set background
-        when(mStyleProvider.createBackground()).thenReturn(new ColorDrawable(12345));
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        assertThat(mFrameAdapter.getView().getBackground()).isNotNull();
-        mFrameAdapter.unbindModel();
-
-        // Re-bind and check that background is unset
-        when(mStyleProvider.createBackground()).thenReturn(null);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        assertThat(mFrameAdapter.getView().getBackground()).isNull();
-    }
-
-    @Test
-    public void testRecycling_inlineSlice() {
-        Element element =
-                Element.newBuilder().setElementList(ElementList.getDefaultInstance()).build();
-        Frame inlineSliceFrame =
-                Frame.newBuilder().addContents(Content.newBuilder().setElement(element)).build();
-        when(mFrameContext.getFrame()).thenReturn(inlineSliceFrame);
-
-        mFrameAdapter.bindModel(
-                inlineSliceFrame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        verify(mAdapterFactory).createAdapterForElement(element, mFrameContext);
-        verify(mElementAdapter).bindModel(element, mFrameContext);
-
-        mFrameAdapter.unbindModel();
-        verify(mAdapterFactory).releaseAdapter(mElementAdapter);
-    }
-
-    @Test
-    public void testRecycling_templateSlice() {
-        String templateId = "bread";
-        Template template = Template.newBuilder().setTemplateId(templateId).build();
-        when(mFrameContext.getTemplate(templateId)).thenReturn(template);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("grain"))
-                        .build();
-        TemplateInvocation templateSlice = TemplateInvocation.newBuilder()
-                                                   .setTemplateId(templateId)
-                                                   .addBindingContexts(bindingContext)
-                                                   .build();
-        Frame templateSliceFrame =
-                Frame.newBuilder()
-                        .addContents(Content.newBuilder().setTemplateInvocation(templateSlice))
-                        .build();
-        when(mFrameContext.getFrame()).thenReturn(templateSliceFrame);
-        mFrameAdapter.bindModel(
-                templateSliceFrame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-        TemplateAdapterModel model = new TemplateAdapterModel(template, bindingContext);
-        verify(mTemplateBinder).createAndBindTemplateAdapter(model, mFrameContext);
-
-        mFrameAdapter.unbindModel();
-        verify(mAdapterFactory).releaseAdapter(mTemplateAdapter);
-    }
-
-    @Test
-    public void testErrorViewReporting() {
-        View warningView = new View(mContext);
-        View errorView = new View(mContext);
-        when(mDebugLogger.getReportView(MessageType.WARNING, mContext)).thenReturn(warningView);
-        when(mDebugLogger.getReportView(MessageType.ERROR, mContext)).thenReturn(errorView);
-
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-
-        // Errors silenced by debug behavior
-        when(mFrameContext.getDebugBehavior()).thenReturn(DebugBehavior.SILENT);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mFrameAdapter.getView().getChildCount()).isEqualTo(1);
-        verify(mDebugLogger, never()).getReportView(anyInt(), any());
-
-        mFrameAdapter.unbindModel();
-
-        // Errors displayed in extra views
-        when(mFrameContext.getDebugBehavior()).thenReturn(DebugBehavior.VERBOSE);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mFrameAdapter.getView().getChildCount()).isEqualTo(3);
-        assertThat(mFrameAdapter.getView().getChildAt(1)).isSameInstanceAs(errorView);
-        assertThat(mFrameAdapter.getView().getChildAt(2)).isSameInstanceAs(warningView);
-
-        mFrameAdapter.unbindModel();
-
-        // No errors
-        when(mDebugLogger.getReportView(MessageType.WARNING, mContext)).thenReturn(null);
-        when(mDebugLogger.getReportView(MessageType.ERROR, mContext)).thenReturn(null);
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mFrameAdapter.getView().getChildCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void testEventLoggerReporting() {
-        List<ErrorCode> errorCodes = new ArrayList<>();
-        errorCodes.add(ErrorCode.ERR_DUPLICATE_STYLE);
-        errorCodes.add(ErrorCode.ERR_POOR_FRAME_RATE);
-        when(mDebugLogger.getErrorCodes()).thenReturn(errorCodes);
-
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        verify(mEventLogger).logEvents(errorCodes);
-    }
-
-    @Test
-    public void testEventLoggerReporting_withFatalError() {
-        List<ErrorCode> errorCodes = new ArrayList<>();
-        errorCodes.add(ErrorCode.ERR_DUPLICATE_STYLE);
-        errorCodes.add(ErrorCode.ERR_POOR_FRAME_RATE);
-        when(mDebugLogger.getErrorCodes()).thenReturn(errorCodes);
-
-        when(mFrameContext.makeStyleFor(any()))
-                .thenThrow(new PietFatalException(ErrorCode.ERR_UNSPECIFIED, "test exception"));
-
-        Frame frame = Frame.newBuilder().addContents(DEFAULT_CONTENT).build();
-
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        verify(mFrameContext).makeStyleFor(any()); // Ensure an exception was thrown
-        verify(mEventLogger).logEvents(errorCodes);
-    }
-
-    @Test
-    public void testUnbindTriggersHideActions() {
-        Action frameHideAction = Action.newBuilder().build();
-        Action elementHideAction = Action.newBuilder().build();
-        Frame frameWithNoActions = Frame.newBuilder()
-                                           .addContents(Content.newBuilder().setElement(
-                                                   Element.newBuilder().setElementStack(
-                                                           ElementStack.getDefaultInstance())))
-                                           .build();
-        Frame frameWithActions =
-                Frame.newBuilder()
-                        .setActions(Actions.newBuilder().addOnHideActions(
-                                VisibilityAction.newBuilder().setAction(frameHideAction)))
-                        .addContents(Content.newBuilder().setElement(
-                                Element.newBuilder()
-                                        .setActions(Actions.newBuilder().addOnHideActions(
-                                                VisibilityAction.newBuilder().setAction(
-                                                        elementHideAction)))
-                                        .setElementStack(ElementStack.getDefaultInstance())))
-                        .build();
-
-        // Bind to a frame with no actions so the onHide actions don't get added to activeActions
-        when(mFrameContext.getFrame()).thenReturn(frameWithNoActions);
-        mFrameAdapter.bindModel(
-                frameWithNoActions, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        when(mFrameContext.getFrame()).thenReturn(frameWithActions);
-        when(mFrameContext.getActionHandler()).thenReturn(mActionHandler);
-        mFrameAdapter.unbindModel();
-
-        verify(mActionHandler)
-                .handleAction(same(frameHideAction), eq(ActionType.VIEW), eq(frameWithActions),
-                        any(View.class), eq(LogData.getDefaultInstance()));
-        verify(mElementAdapter).triggerHideActions(mFrameContext);
-    }
-
-    @Test
-    public void testBindModel_callsOnBindLogDataCallback() {
-        Frame frame =
-                Frame.newBuilder().addContents(DEFAULT_CONTENT).setLogData(mLogDataTest).build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        assertThat(mCallbackBound).isTrue();
-    }
-
-    @Test
-    public void testUnbindModel_callsOnBindLogDataCallback() {
-        Frame frame =
-                Frame.newBuilder().addContents(DEFAULT_CONTENT).setLogData(mLogDataTest).build();
-        when(mFrameContext.getFrame()).thenReturn(frame);
-        mFrameAdapter.bindModel(frame, FRAME_WIDTH, (ShardingControl) null, mPietSharedStates);
-
-        when(mFrameContext.getActionHandler()).thenReturn(mActionHandler);
-        mFrameAdapter.unbindModel();
-        assertThat(mCallbackUnbound).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameContextTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameContextTest.java
deleted file mode 100644
index 5bf77c22..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameContextTest.java
+++ /dev/null
@@ -1,1313 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.widget.FrameLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Actions;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ActionsBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ChunkedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.CustomBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ElementBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.GridCellWidthBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ImageBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.LogDataBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.TemplateBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.VisibilityBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingContext;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElementData;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridCellWidth;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.HostBindingData;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TemplateInvocation;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.ColorStop;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.Fill;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.LinearGradient;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.ImageSource;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.ComparisonCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition.DarkLightMode;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.FrameWidthCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.MediaQueryCondition;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheets;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.BoundStyle;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.Chunk;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ChunkedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.StyledTextChunk;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Tests of the {@link FrameContext}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FrameContextTest {
-    private static final String DEFAULT_TEMPLATE_ID = "TEMPLATE_ID";
-    private static final Template DEFAULT_TEMPLATE =
-            Template.newBuilder().setTemplateId(DEFAULT_TEMPLATE_ID).build();
-    private static final Frame DEFAULT_FRAME =
-            Frame.newBuilder().addTemplates(DEFAULT_TEMPLATE).build();
-    private static final String SAMPLE_STYLE_ID = "STYLE_ID";
-    private static final int BASE_STYLE_COLOR = 111111;
-    private static final int SAMPLE_STYLE_COLOR = 888888;
-    private static final Style SAMPLE_STYLE =
-            Style.newBuilder().setStyleId(SAMPLE_STYLE_ID).setColor(SAMPLE_STYLE_COLOR).build();
-    private static final String STYLESHEET_ID = "STYLESHEET_ID";
-    private static final Style BASE_STYLE = Style.newBuilder().setColor(BASE_STYLE_COLOR).build();
-    private static final StyleIdsStack SAMPLE_STYLE_IDS =
-            StyleIdsStack.newBuilder().addStyleIds(SAMPLE_STYLE_ID).build();
-    private static final String BINDING_ID = "BINDING_ID";
-    private static final String INVALID_BINDING_ID = "NOT_A_REAL_BINDING_ID";
-    private static final Map<String, Template> DEFAULT_TEMPLATES = new HashMap<>();
-    private static final int FRAME_WIDTH_PX = 999999;
-
-    static {
-        DEFAULT_TEMPLATES.put(DEFAULT_TEMPLATE_ID, DEFAULT_TEMPLATE);
-    }
-
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private final Map<String, Style> mDefaultStylesheet = new HashMap<>();
-    private final DebugLogger mDebugLogger = new DebugLogger();
-
-    private HostBindingProvider mHostBindingProvider;
-    private HostProviders mHostProviders;
-    private FrameContext mFrameContext;
-    private List<PietSharedState> mPietSharedStates;
-    private PietStylesHelper mPietStylesHelper;
-    private StyleProvider mDefaultStyleProvider;
-    private Context mContext;
-    private FrameLayout mFrameView;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mHostBindingProvider = new HostBindingProvider();
-        mHostProviders = new HostProviders(
-                mAssetProvider, mock(CustomElementProvider.class), mHostBindingProvider, null);
-        mDefaultStyleProvider = new StyleProvider(mAssetProvider);
-
-        mDefaultStylesheet.put(SAMPLE_STYLE_ID, SAMPLE_STYLE);
-        mPietSharedStates = new ArrayList<>();
-        mPietStylesHelper = newPietStylesHelper();
-
-        mFrameView = new FrameLayout(mContext);
-    }
-
-    @Test
-    public void testSharedStateTemplatesAddedToFrame() {
-        String sharedStateTemplateId = "SHARED_STATE_TEMPLATE_ID";
-        Template sharedStateTemplate =
-                Template.newBuilder().setTemplateId(sharedStateTemplateId).build();
-        mPietSharedStates.add(
-                PietSharedState.newBuilder().addTemplates(sharedStateTemplate).build());
-        mFrameContext = defaultFrameContext();
-
-        assertThat(mFrameContext.getTemplate(sharedStateTemplateId))
-                .isSameInstanceAs(sharedStateTemplate);
-        assertThat(mFrameContext.getTemplate(DEFAULT_TEMPLATE_ID))
-                .isSameInstanceAs(DEFAULT_TEMPLATE);
-    }
-
-    @Test
-    public void testThrowsIfSharedStateTemplatesConflictWithFrameTemplates() {
-        Template sharedStateTemplate =
-                Template.newBuilder().setTemplateId(DEFAULT_TEMPLATE_ID).build();
-        mPietSharedStates.add(
-                PietSharedState.newBuilder().addTemplates(sharedStateTemplate).build());
-        assertThatRunnable(this::defaultFrameContext)
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Template key 'TEMPLATE_ID' already defined");
-    }
-
-    @Test
-    public void testThrowsIfDuplicateBindingValueId() {
-        mFrameContext = defaultFrameContext();
-        BindingContext bindingContextWithDuplicateIds =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId(BINDING_ID))
-                        .addBindingValues(BindingValue.newBuilder().setBindingId(BINDING_ID))
-                        .build();
-        assertThatRunnable(()
-                                   -> mFrameContext.createTemplateContext(
-                                           DEFAULT_TEMPLATE, bindingContextWithDuplicateIds))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("BindingValue key 'BINDING_ID' already defined");
-    }
-
-    @Test
-    public void testGetters() {
-        String template2BindingId = "TEMPLATE_2";
-        Template template2 = Template.newBuilder().setTemplateId(template2BindingId).build();
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template2).build();
-        mPietSharedStates.add(sharedState);
-        mFrameContext = new FrameContext(DEFAULT_FRAME, mDefaultStylesheet, mPietSharedStates,
-                newPietStylesHelper(), DebugBehavior.VERBOSE, mDebugLogger, mActionHandler,
-                mHostProviders, mFrameView);
-
-        assertThat(mFrameContext.getFrame()).isEqualTo(DEFAULT_FRAME);
-        assertThat(mFrameContext.getActionHandler()).isEqualTo(mActionHandler);
-
-        assertThat(mFrameContext.getTemplate(DEFAULT_TEMPLATE_ID)).isEqualTo(DEFAULT_TEMPLATE);
-        assertThat(mFrameContext.getTemplate(template2BindingId)).isEqualTo(template2);
-    }
-
-    @Test
-    public void testThrowsWithNoBindingContext() {
-        mFrameContext = makeFrameContextForDefaultFrame();
-
-        assertThatRunnable(
-                () -> mFrameContext.getActionsFromBinding(ActionsBindingRef.getDefaultInstance()))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("no BindingValues defined");
-    }
-
-    @Test
-    public void testCreateTemplateContext_lotsOfStuff() {
-        mFrameContext = defaultFrameContext();
-
-        // createTemplateContext should add new BindingValues
-        ParameterizedText text = ParameterizedText.newBuilder().setText("Calico").build();
-        BindingValue textBinding = BindingValue.newBuilder()
-                                           .setBindingId(BINDING_ID)
-                                           .setParameterizedText(text)
-                                           .build();
-        FrameContext frameContextWithBindings =
-                mFrameContext.createTemplateContext(DEFAULT_TEMPLATE,
-                        BindingContext.newBuilder().addBindingValues(textBinding).build());
-        assertThat(
-                frameContextWithBindings
-                        .getParameterizedTextBindingValue(ParameterizedTextBindingRef.newBuilder()
-                                                                  .setBindingId(BINDING_ID)
-                                                                  .build())
-                        .getParameterizedText())
-                .isEqualTo(text);
-
-        // and clear out all the previous styles
-        assertThat(frameContextWithBindings.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .isEqualTo(mDefaultStyleProvider);
-        assertThat(frameContextWithBindings.makeStyleFor(SAMPLE_STYLE_IDS).getColor())
-                .isEqualTo(mDefaultStyleProvider.getColor());
-    }
-
-    @Test
-    public void testCreateTemplateContext_stylesheetId() {
-        String styleId = "cotton";
-        int width = 343;
-        Style style = Style.newBuilder().setStyleId(styleId).setWidth(width).build();
-        String stylesheetId = "linen";
-        Stylesheet stylesheet =
-                Stylesheet.newBuilder().setStylesheetId(stylesheetId).addStyles(style).build();
-        mPietSharedStates.add(PietSharedState.newBuilder().addStylesheets(stylesheet).build());
-        mFrameContext = defaultFrameContext();
-
-        Template template =
-                Template.newBuilder()
-                        .setTemplateId("kingSize")
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds(stylesheetId))
-                        .build();
-
-        FrameContext templateContext =
-                mFrameContext.createTemplateContext(template, BindingContext.getDefaultInstance());
-
-        StyleIdsStack styleRef = StyleIdsStack.newBuilder().addStyleIds(styleId).build();
-        assertThat(templateContext.makeStyleFor(styleRef).getWidthSpecPx(mContext))
-                .isEqualTo((int) LayoutUtils.dpToPx(width, mContext));
-    }
-
-    @Test
-    public void testCreateTemplateContext_multipleStylesheets() {
-        String styleId = "cotton";
-        int width = 343;
-        Style style = Style.newBuilder().setStyleId(styleId).setWidth(width).build();
-        String stylesheetId = "linen";
-        Stylesheet stylesheet =
-                Stylesheet.newBuilder().setStylesheetId(stylesheetId).addStyles(style).build();
-        mPietSharedStates.add(PietSharedState.newBuilder().addStylesheets(stylesheet).build());
-        mFrameContext = defaultFrameContext();
-        int templateStyleColor = 343434;
-        String templateStyleId = "templateStyle";
-
-        Template template =
-                Template.newBuilder()
-                        .setTemplateId("kingSize")
-                        .setStylesheets(Stylesheets.newBuilder()
-                                                .addStylesheetIds(stylesheetId)
-                                                .addStylesheets(Stylesheet.newBuilder().addStyles(
-                                                        Style.newBuilder()
-                                                                .setStyleId(templateStyleId)
-                                                                .setColor(templateStyleColor))))
-                        .build();
-
-        FrameContext templateContext =
-                mFrameContext.createTemplateContext(template, BindingContext.getDefaultInstance());
-
-        StyleIdsStack styleRef = StyleIdsStack.newBuilder().addStyleIds(styleId).build();
-        assertThat(templateContext.makeStyleFor(styleRef).getWidthSpecPx(mContext))
-                .isEqualTo((int) LayoutUtils.dpToPx(width, mContext));
-        styleRef = StyleIdsStack.newBuilder().addStyleIds(templateStyleId).build();
-        assertThat(templateContext.makeStyleFor(styleRef).getColor()).isEqualTo(templateStyleColor);
-    }
-
-    @Test
-    public void testCreateTemplateContext_transcludingBinding() {
-        String parentBindingId = "PARENT";
-        String childBindingId = "CHILD";
-        ParameterizedText parentBoundText =
-                ParameterizedText.newBuilder().setText("parent_text").build();
-        BindingValue parentBindingValue = BindingValue.newBuilder()
-                                                  .setBindingId(parentBindingId)
-                                                  .setParameterizedText(parentBoundText)
-                                                  .build();
-        ParameterizedTextBindingRef childTextBindingRef =
-                ParameterizedTextBindingRef.newBuilder().setBindingId(childBindingId).build();
-        mFrameContext = makeFrameContextWithBinding(parentBindingValue);
-        FrameContext childContext = mFrameContext.createTemplateContext(
-                Template.newBuilder()
-                        .setElement(Element.newBuilder().setTextElement(
-                                TextElement.newBuilder().setParameterizedTextBinding(
-                                        childTextBindingRef)))
-                        .build(),
-                BindingContext.newBuilder()
-                        .addBindingValues(
-                                BindingValue.newBuilder()
-                                        .setBindingId(childBindingId)
-                                        .setBindingIdFromTranscludingTemplate(parentBindingId))
-                        .build());
-
-        assertThat(childContext.getParameterizedTextBindingValue(childTextBindingRef))
-                .isEqualTo(BindingValue.newBuilder()
-                                   .setBindingId(childBindingId)
-                                   .setParameterizedText(parentBoundText)
-                                   .build());
-    }
-
-    @Test
-    public void testCreateTemplateContext_transcludingBindingNotFound() {
-        String invalidParentBindingId = "NOT_FOUND";
-        String childBindingId = "CHILD";
-        ParameterizedTextBindingRef childTextBindingRef =
-                ParameterizedTextBindingRef.newBuilder().setBindingId(childBindingId).build();
-        mFrameContext = makeFrameContextWithNoBindings();
-        FrameContext childContext = mFrameContext.createTemplateContext(
-                Template.newBuilder()
-                        .setElement(Element.newBuilder().setTextElement(
-                                TextElement.newBuilder().setParameterizedTextBinding(
-                                        childTextBindingRef)))
-                        .build(),
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder()
-                                                  .setBindingId(childBindingId)
-                                                  .setBindingIdFromTranscludingTemplate(
-                                                          invalidParentBindingId))
-                        .build());
-
-        assertThat(mDebugLogger.getMessages(MessageType.ERROR)).isEmpty();
-        assertThatRunnable(() -> childContext.getParameterizedTextBindingValue(childTextBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Parameterized text binding not found for CHILD");
-    }
-
-    @Test
-    public void testCreateTemplateContext_optionalTranscludingBindingNotFound() {
-        String invalidParentBindingId = "NOT_FOUND";
-        String childBindingId = "CHILD";
-        ParameterizedTextBindingRef childTextBindingRef = ParameterizedTextBindingRef.newBuilder()
-                                                                  .setBindingId(childBindingId)
-                                                                  .setIsOptional(true)
-                                                                  .build();
-        mFrameContext = makeFrameContextWithNoBindings();
-        FrameContext childContext = mFrameContext.createTemplateContext(
-                Template.newBuilder()
-                        .setElement(Element.newBuilder().setTextElement(
-                                TextElement.newBuilder().setParameterizedTextBinding(
-                                        childTextBindingRef)))
-                        .build(),
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder()
-                                                  .setBindingId(childBindingId)
-                                                  .setBindingIdFromTranscludingTemplate(
-                                                          invalidParentBindingId))
-                        .build());
-
-        assertThat(mDebugLogger.getMessages(MessageType.ERROR)).isEmpty();
-        assertThat(childContext.getParameterizedTextBindingValue(childTextBindingRef))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testMakeStyleFor() {
-        mFrameContext = defaultFrameContext();
-
-        // Returns base style provider if there are no styles defined
-        StyleProvider noStyle = mFrameContext.makeStyleFor(StyleIdsStack.getDefaultInstance());
-        assertThat(noStyle).isEqualTo(mDefaultStyleProvider);
-
-        // Successful lookup results in a new style provider
-        StyleProvider defaultStyle = mFrameContext.makeStyleFor(SAMPLE_STYLE_IDS);
-        assertThat(defaultStyle.getColor()).isEqualTo(SAMPLE_STYLE_COLOR);
-
-        // Failed lookup returns the current style provider
-        StyleProvider notFoundStyle = mFrameContext.makeStyleFor(
-                StyleIdsStack.newBuilder().addStyleIds(INVALID_BINDING_ID).build());
-        assertThat(notFoundStyle).isEqualTo(mDefaultStyleProvider);
-    }
-
-    @Test
-    public void testGetText() {
-        ParameterizedText text = ParameterizedText.newBuilder().setText("tabby").build();
-        BindingValue textBindingValue = defaultBinding().setParameterizedText(text).build();
-        ParameterizedTextBindingRef textBindingRef =
-                ParameterizedTextBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(textBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getParameterizedTextBindingValue(textBindingRef))
-                .isEqualTo(textBindingValue);
-
-        // Can't look up binding
-        assertThatRunnable(()
-                                   -> mFrameContext.getParameterizedTextBindingValue(
-                                           ParameterizedTextBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Parameterized text binding not found");
-
-        // Binding has no content
-        assertThatRunnable(
-                ()
-                        -> makeFrameContextWithNoBindings().getParameterizedTextBindingValue(
-                                textBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Parameterized text binding not found");
-
-        // Binding is invalid but is optional
-        ParameterizedTextBindingRef textBindingRefInvalidOptional =
-                ParameterizedTextBindingRef.newBuilder()
-                        .setBindingId(INVALID_BINDING_ID)
-                        .setIsOptional(true)
-                        .build();
-        assertThat(makeFrameContextWithNoBindings().getParameterizedTextBindingValue(
-                           textBindingRefInvalidOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-
-        // Binding has no content but is optional
-        ParameterizedTextBindingRef textBindingRefOptional =
-                ParameterizedTextBindingRef.newBuilder()
-                        .setBindingId(BINDING_ID)
-                        .setIsOptional(true)
-                        .build();
-        assertThat(makeFrameContextWithNoBindings().getParameterizedTextBindingValue(
-                           textBindingRefOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetText_hostBinding() {
-        ParameterizedText text = ParameterizedText.newBuilder().setText("tabby").build();
-        BindingValue textBindingValue = defaultBinding().setParameterizedText(text).build();
-        BindingValue hostTextBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setParameterizedText(text)
-                        .build();
-        ParameterizedTextBindingRef textBindingRef =
-                ParameterizedTextBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostTextBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getParameterizedTextBindingValue(textBindingRef))
-                .isEqualTo(textBindingValue);
-    }
-
-    @Test
-    public void testGetImage() {
-        Image image =
-                Image.newBuilder().addSources(ImageSource.newBuilder().setUrl("myUrl")).build();
-        BindingValue imageBindingValue = defaultBinding().setImage(image).build();
-        ImageBindingRef imageBindingRef =
-                ImageBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(imageBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getImageBindingValue(imageBindingRef))
-                .isEqualTo(imageBindingValue);
-
-        // Can't look up binding
-        assertThatRunnable(()
-                                   -> mFrameContext.getImageBindingValue(
-                                           ImageBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Image binding not found");
-
-        // Binding has no content
-        assertThatRunnable(
-                () -> makeFrameContextWithNoBindings().getImageBindingValue(imageBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Image binding not found");
-
-        // Binding is invalid but is optional
-        ImageBindingRef imageBindingRefInvalidOptional = ImageBindingRef.newBuilder()
-                                                                 .setBindingId(INVALID_BINDING_ID)
-                                                                 .setIsOptional(true)
-                                                                 .build();
-        assertThat(makeFrameContextWithNoBindings().getImageBindingValue(
-                           imageBindingRefInvalidOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-
-        // Binding has no content but is optional
-        ImageBindingRef imageBindingRefOptional =
-                ImageBindingRef.newBuilder().setBindingId(BINDING_ID).setIsOptional(true).build();
-        assertThat(makeFrameContextWithNoBindings().getImageBindingValue(imageBindingRefOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetImage_hostBinding() {
-        Image image =
-                Image.newBuilder().addSources(ImageSource.newBuilder().setUrl("myUrl")).build();
-        BindingValue imageBindingValue = defaultBinding().setImage(image).build();
-        BindingValue hostImageBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setImage(image)
-                        .build();
-        ImageBindingRef imageBindingRef =
-                ImageBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostImageBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getImageBindingValue(imageBindingRef))
-                .isEqualTo(imageBindingValue);
-    }
-
-    @Test
-    public void testGetElement() {
-        Element element = Element.newBuilder()
-                                  .setStyleReferences(StyleIdsStack.newBuilder().addStyleIds("el"))
-                                  .build();
-        BindingValue elementBindingValue = defaultBinding().setElement(element).build();
-        ElementBindingRef elementBindingRef =
-                ElementBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(elementBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getElementBindingValue(elementBindingRef))
-                .isEqualTo(elementBindingValue);
-
-        // Can't look up binding
-        assertThatRunnable(()
-                                   -> mFrameContext.getElementBindingValue(
-                                           ElementBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Element binding not found");
-
-        // Binding has no content
-        assertThatRunnable(
-                () -> makeFrameContextWithNoBindings().getElementBindingValue(elementBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Element binding not found");
-
-        // Binding is missing but is optional
-        ElementBindingRef elementBindingRefInvalidOptional =
-                ElementBindingRef.newBuilder()
-                        .setBindingId(INVALID_BINDING_ID)
-                        .setIsOptional(true)
-                        .build();
-        assertThat(makeFrameContextWithNoBindings().getElementBindingValue(
-                           elementBindingRefInvalidOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-
-        // Binding has no content but is optional
-        ElementBindingRef elementBindingRefOptional =
-                ElementBindingRef.newBuilder().setBindingId(BINDING_ID).setIsOptional(true).build();
-        assertThat(
-                makeFrameContextWithNoBindings().getElementBindingValue(elementBindingRefOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetElement_hostBinding() {
-        Element element = Element.newBuilder()
-                                  .setStyleReferences(StyleIdsStack.newBuilder().addStyleIds("el"))
-                                  .build();
-        BindingValue elementBindingValue = defaultBinding().setElement(element).build();
-        BindingValue hostListBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setElement(element)
-                        .build();
-        ElementBindingRef elementBindingRef =
-                ElementBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostListBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getElementBindingValue(elementBindingRef))
-                .isEqualTo(elementBindingValue);
-    }
-
-    @Test
-    public void testGetVisibility() {
-        Visibility visibility = Visibility.INVISIBLE;
-        BindingValue visibilityBindingValue = defaultBinding().setVisibility(visibility).build();
-        VisibilityBindingRef visibilityBindingRef =
-                VisibilityBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(visibilityBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getVisibilityFromBinding(visibilityBindingRef))
-                .isEqualTo(visibility);
-
-        // Can't look up binding
-        assertThat(
-                mFrameContext.getVisibilityFromBinding(
-                        VisibilityBindingRef.newBuilder().setBindingId(INVALID_BINDING_ID).build()))
-                .isNull();
-
-        // Binding has no content
-        assertThat(makeFrameContextWithNoBindings().getVisibilityFromBinding(visibilityBindingRef))
-                .isNull();
-    }
-
-    @Test
-    public void testGetVisibility_hostBinding() {
-        Visibility visibility = Visibility.INVISIBLE;
-        BindingValue hostListBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setVisibility(visibility)
-                        .build();
-        VisibilityBindingRef visibilityBindingRef =
-                VisibilityBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostListBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getVisibilityFromBinding(visibilityBindingRef))
-                .isEqualTo(visibility);
-    }
-
-    @Test
-    public void testGetGridCellWidthFromBinding() {
-        GridCellWidth cellWidth = GridCellWidth.newBuilder().setWeight(123).build();
-        mFrameContext =
-                makeFrameContextWithBinding(defaultBinding().setCellWidth(cellWidth).build());
-        assertThat(mFrameContext.getGridCellWidthFromBinding(
-                           GridCellWidthBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(cellWidth);
-        assertThat(
-                mFrameContext.getGridCellWidthFromBinding(GridCellWidthBindingRef.newBuilder()
-                                                                  .setBindingId(INVALID_BINDING_ID)
-                                                                  .build()))
-                .isNull();
-
-        mFrameContext = makeFrameContextWithNoBindings();
-        assertThat(mFrameContext.getGridCellWidthFromBinding(
-                           GridCellWidthBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isNull();
-    }
-
-    @Test
-    public void testGetGridCellWidthFromBinding_hostBinding() {
-        GridCellWidth cellWidth = GridCellWidth.newBuilder().setWeight(123).build();
-        mFrameContext = makeFrameContextWithBinding(
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setCellWidth(cellWidth)
-                        .build());
-        assertThat(mFrameContext.getGridCellWidthFromBinding(
-                           GridCellWidthBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(cellWidth);
-    }
-
-    @Test
-    public void testGetActionsFromBinding() {
-        Actions actions = Actions.newBuilder().build();
-        mFrameContext = makeFrameContextWithBinding(defaultBinding().setActions(actions).build());
-        assertThat(mFrameContext.getActionsFromBinding(
-                           ActionsBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isSameInstanceAs(actions);
-        assertThat(mFrameContext.getActionsFromBinding(
-                           ActionsBindingRef.newBuilder().setBindingId(INVALID_BINDING_ID).build()))
-                .isSameInstanceAs(Actions.getDefaultInstance());
-
-        mFrameContext = makeFrameContextWithNoBindings();
-        assertThat(mFrameContext.getActionsFromBinding(
-                           ActionsBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isSameInstanceAs(Actions.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetActionsFromBinding_hostBinding() {
-        mFrameContext = makeFrameContextWithBinding(
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setActions(Actions.getDefaultInstance())
-                        .build());
-        assertThat(mFrameContext.getActionsFromBinding(
-                           ActionsBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(Actions.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetLogDataFromBinding() {
-        LogData logData = LogData.newBuilder().build();
-        mFrameContext = makeFrameContextWithBinding(defaultBinding().setLogData(logData).build());
-        assertThat(mFrameContext.getLogDataFromBinding(
-                           LogDataBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isSameInstanceAs(logData);
-
-        assertThat(mFrameContext.getLogDataFromBinding(
-                           LogDataBindingRef.newBuilder().setBindingId(INVALID_BINDING_ID).build()))
-                .isNull();
-
-        mFrameContext = makeFrameContextWithNoBindings();
-        assertThat(mFrameContext.getLogDataFromBinding(
-                           LogDataBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isNull();
-    }
-
-    @Test
-    public void testGetLogDataFromBinding_hostBinding() {
-        mFrameContext = makeFrameContextWithBinding(
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setLogData(LogData.getDefaultInstance())
-                        .build());
-        assertThat(mFrameContext.getLogDataFromBinding(
-                           LogDataBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(LogData.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetStyleFromBinding() {
-        BoundStyle boundStyle = BoundStyle.newBuilder().setColor(12345).build();
-        mFrameContext =
-                makeFrameContextWithBinding(defaultBinding().setBoundStyle(boundStyle).build());
-        assertThat(mFrameContext.getStyleFromBinding(
-                           StyleBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(boundStyle);
-        assertThat(mFrameContext.getStyleFromBinding(
-                           StyleBindingRef.newBuilder().setBindingId(INVALID_BINDING_ID).build()))
-                .isEqualTo(BoundStyle.getDefaultInstance());
-
-        mFrameContext = makeFrameContextWithNoBindings();
-        assertThat(mFrameContext.getStyleFromBinding(
-                           StyleBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(BoundStyle.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetStyleFromBinding_hostBinding() {
-        BoundStyle boundStyle = BoundStyle.newBuilder().setColor(12345).build();
-        mFrameContext = makeFrameContextWithBinding(
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setBoundStyle(boundStyle)
-                        .build());
-        assertThat(mFrameContext.getStyleFromBinding(
-                           StyleBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(boundStyle);
-    }
-
-    @Test
-    public void testGetTemplateInvocationFromBinding() {
-        TemplateInvocation templateInvocation =
-                TemplateInvocation.newBuilder().setTemplateId("carboncopy").build();
-        BindingValue templateBindingValue =
-                defaultBinding().setTemplateInvocation(templateInvocation).build();
-        mFrameContext = makeFrameContextWithBinding(templateBindingValue);
-        assertThat(mFrameContext.getTemplateInvocationBindingValue(
-                           TemplateBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .isEqualTo(templateBindingValue);
-        assertThat(mFrameContext.getTemplateInvocationBindingValue(
-                           TemplateBindingRef.newBuilder()
-                                   .setIsOptional(true)
-                                   .setBindingId(INVALID_BINDING_ID)
-                                   .build()))
-                .isEqualTo(BindingValue.getDefaultInstance());
-        assertThatRunnable(()
-                                   -> mFrameContext.getTemplateInvocationBindingValue(
-                                           TemplateBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Template binding not found for NOT_A_REAL_BINDING_ID");
-
-        mFrameContext = makeFrameContextWithNoBindings();
-        assertThatRunnable(
-                ()
-                        -> mFrameContext.getTemplateInvocationBindingValue(
-                                TemplateBindingRef.newBuilder().setBindingId(BINDING_ID).build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Template binding not found for BINDING_ID");
-    }
-
-    @Test
-    public void testGetTemplateInvocationFromBinding_hostBinding() {
-        TemplateInvocation templateInvocation =
-                TemplateInvocation.newBuilder().setTemplateId("carboncopy").build();
-        mFrameContext = makeFrameContextWithBinding(
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setTemplateInvocation(templateInvocation)
-                        .build());
-    }
-
-    @Test
-    public void testGetCustomElementBindingValue() {
-        CustomElementData customElement = CustomElementData.getDefaultInstance();
-        BindingValue customElementBindingValue =
-                defaultBinding().setCustomElementData(customElement).build();
-        CustomBindingRef customBindingRef =
-                CustomBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(customElementBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getCustomElementBindingValue(customBindingRef))
-                .isEqualTo(customElementBindingValue);
-
-        // Can't look up binding
-        assertThatRunnable(()
-                                   -> mFrameContext.getCustomElementBindingValue(
-                                           CustomBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Custom element binding not found");
-
-        // Binding has no content
-        assertThatRunnable(()
-                                   -> makeFrameContextWithNoBindings().getCustomElementBindingValue(
-                                           customBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Custom element binding not found");
-
-        // Binding is missing but is optional
-        CustomBindingRef customBindingRefInvalidOptional = CustomBindingRef.newBuilder()
-                                                                   .setBindingId(INVALID_BINDING_ID)
-                                                                   .setIsOptional(true)
-                                                                   .build();
-        assertThat(makeFrameContextWithNoBindings().getCustomElementBindingValue(
-                           customBindingRefInvalidOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-
-        // Binding has no content but is optional
-        CustomBindingRef customBindingRefOptional =
-                CustomBindingRef.newBuilder().setBindingId(BINDING_ID).setIsOptional(true).build();
-        assertThat(makeFrameContextWithNoBindings().getCustomElementBindingValue(
-                           customBindingRefOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetCustomElementBindingValue_hostBinding() {
-        CustomElementData customElement = CustomElementData.getDefaultInstance();
-        BindingValue customElementBindingValue =
-                defaultBinding().setCustomElementData(customElement).build();
-        BindingValue hostCustomElementBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setCustomElementData(customElement)
-                        .build();
-        CustomBindingRef customBindingRef =
-                CustomBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostCustomElementBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getCustomElementBindingValue(customBindingRef))
-                .isEqualTo(customElementBindingValue);
-    }
-
-    @Test
-    public void testGetChunkedTextBindingValue() {
-        ChunkedText text = ChunkedText.newBuilder()
-                                   .addChunks(Chunk.newBuilder().setTextChunk(
-                                           StyledTextChunk.newBuilder().setParameterizedText(
-                                                   ParameterizedText.newBuilder().setText("text"))))
-                                   .build();
-        BindingValue textBindingValue = defaultBinding().setChunkedText(text).build();
-        ChunkedTextBindingRef textBindingRef =
-                ChunkedTextBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(textBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getChunkedTextBindingValue(textBindingRef))
-                .isEqualTo(textBindingValue);
-
-        // Can't look up binding
-        assertThatRunnable(()
-                                   -> mFrameContext.getChunkedTextBindingValue(
-                                           ChunkedTextBindingRef.newBuilder()
-                                                   .setBindingId(INVALID_BINDING_ID)
-                                                   .build()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Chunked text binding not found");
-
-        // Binding has no content
-        assertThatRunnable(
-                () -> makeFrameContextWithNoBindings().getChunkedTextBindingValue(textBindingRef))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Chunked text binding not found");
-
-        // Binding is missing but is optional
-        ChunkedTextBindingRef textBindingRefInvalidOptional =
-                ChunkedTextBindingRef.newBuilder()
-                        .setBindingId(INVALID_BINDING_ID)
-                        .setIsOptional(true)
-                        .build();
-        assertThat(makeFrameContextWithNoBindings().getChunkedTextBindingValue(
-                           textBindingRefInvalidOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-
-        // Binding has no content but is optional
-        ChunkedTextBindingRef textBindingRefOptional = ChunkedTextBindingRef.newBuilder()
-                                                               .setBindingId(BINDING_ID)
-                                                               .setIsOptional(true)
-                                                               .build();
-        assertThat(
-                makeFrameContextWithNoBindings().getChunkedTextBindingValue(textBindingRefOptional))
-                .isEqualTo(BindingValue.getDefaultInstance());
-    }
-
-    @Test
-    public void testGetChunkedTextBindingValue_hostBinding() {
-        ChunkedText text = ChunkedText.newBuilder()
-                                   .addChunks(Chunk.newBuilder().setTextChunk(
-                                           StyledTextChunk.newBuilder().setParameterizedText(
-                                                   ParameterizedText.newBuilder().setText("text"))))
-                                   .build();
-        BindingValue textBindingValue = defaultBinding().setChunkedText(text).build();
-        BindingValue hostTextBindingValue =
-                defaultBinding()
-                        .setHostBindingData(HostBindingData.newBuilder())
-                        .setChunkedText(text)
-                        .build();
-        ChunkedTextBindingRef textBindingRef =
-                ChunkedTextBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-
-        mFrameContext = makeFrameContextWithBinding(hostTextBindingValue);
-
-        // Succeed in looking up binding
-        assertThat(mFrameContext.getChunkedTextBindingValue(textBindingRef))
-                .isEqualTo(textBindingValue);
-    }
-
-    @Test
-    public void testCreateWithoutError() {
-        Frame frame = getWorkingFrame();
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-        assertThat(frameContext).isNotNull();
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_withStylesheetId() {
-        Frame frame =
-                Frame.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID))
-                        .build();
-        setUpPietSharedStates();
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        // The style is not currently bound, but available from the stylesheet.
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()).getColor())
-                .isNotEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext.makeStyleFor(SAMPLE_STYLE_IDS).getColor())
-                .isEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_withStylesheet() {
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                      Stylesheet.newBuilder().addStyles(SAMPLE_STYLE)))
-                              .build();
-
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        // The style is not currently bound, but available from the stylesheet.
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()).getColor())
-                .isNotEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext.makeStyleFor(SAMPLE_STYLE_IDS).getColor())
-                .isEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_withMultipleStylesheets() {
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder()
-                                                      .addStylesheetIds(STYLESHEET_ID)
-                                                      .addStylesheets(Stylesheet.newBuilder()
-                                                                              .addStyles(BASE_STYLE)
-                                                                              .build()))
-                              .build();
-
-        setUpPietSharedStates();
-
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()).getColor())
-                .isNotEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext.makeStyleFor(SAMPLE_STYLE_IDS).getColor())
-                .isEqualTo(SAMPLE_STYLE_COLOR);
-        assertThat(frameContext
-                           .makeStyleFor(StyleIdsStack.newBuilder()
-                                                 .addStyleIds(BASE_STYLE.getStyleId())
-                                                 .build())
-                           .getColor())
-                .isEqualTo(BASE_STYLE_COLOR);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_withFrameStyle() {
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                      Stylesheet.newBuilder().addStyles(SAMPLE_STYLE)))
-                              .setStyleReferences(SAMPLE_STYLE_IDS)
-                              .build();
-
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .isEqualTo(mDefaultStyleProvider);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_withoutFrameStyle() {
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                      Stylesheet.newBuilder().addStyles(SAMPLE_STYLE)))
-                              .build();
-
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .isEqualTo(mDefaultStyleProvider);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testBindFrame_baseStyle() {
-        int styleHeight = 747;
-        String heightStyleId = "JUMBO";
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                      Stylesheet.newBuilder()
-                                              .addStyles(SAMPLE_STYLE)
-                                              .addStyles(Style.newBuilder()
-                                                                 .setStyleId(heightStyleId)
-                                                                 .setHeight(styleHeight))))
-                              .setStyleReferences(SAMPLE_STYLE_IDS)
-                              .build();
-
-        // Set up a frame with a color applied to the frame.
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-        StyleProvider baseStyleWithHeight = frameContext.makeStyleFor(
-                StyleIdsStack.newBuilder().addStyleIds(heightStyleId).build());
-
-        // Make a style for something that doesn't override color, and check that the base color is
-        // a default, not the frame color.
-        assertThat(baseStyleWithHeight.getColor()).isEqualTo(Style.getDefaultInstance().getColor());
-        assertThat(baseStyleWithHeight.getHeightSpecPx(mContext))
-                .isEqualTo((int) LayoutUtils.dpToPx(styleHeight, mContext));
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testMergeStyleIdsStack() {
-        String styleId1 = "STYLE1";
-        Style style1 = Style.newBuilder()
-                               .setColor(12345) // Not overridden
-                               .setMaxLines(54321) // Overridden
-                               .setFont(Font.newBuilder().setSize(11).setItalic(true))
-                               .setBackground(Fill.newBuilder().setLinearGradient(
-                                       LinearGradient.newBuilder().addStops(
-                                               ColorStop.newBuilder().setColor(1234))))
-                               .setStyleId(styleId1)
-                               .build();
-        String styleId2 = "STYLE2";
-        Style style2 = Style.newBuilder()
-                               .setMaxLines(22222) // Overrides
-                               .setMinHeight(33333) // Not an override
-                               .setFont(Font.newBuilder().setSize(13))
-                               .setBackground(Fill.newBuilder().setLinearGradient(
-                                       LinearGradient.newBuilder().setDirectionDeg(321)))
-                               .setStyleId(styleId2)
-                               .build();
-        StyleIdsStack twoStyles =
-                StyleIdsStack.newBuilder().addStyleIds(styleId1).addStyleIds(styleId2).build();
-        Frame frame =
-                Frame.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID))
-                        .setStyleReferences(twoStyles)
-                        .build();
-        mPietSharedStates.add(PietSharedState.newBuilder()
-                                      .addStylesheets(Stylesheet.newBuilder()
-                                                              .setStylesheetId(STYLESHEET_ID)
-                                                              .addStyles(style1)
-                                                              .addStyles(style2)
-                                                              .build())
-                                      .build());
-
-        FrameContext frameContext = makeFrameContextFromFrame(frame);
-
-        StyleProvider defaultFrameStyle = frameContext.makeStyleFor(twoStyles);
-        assertThat(defaultFrameStyle.getColor()).isEqualTo(12345);
-        assertThat(defaultFrameStyle.getMaxLines()).isEqualTo(22222);
-        assertThat(defaultFrameStyle.getMinHeight()).isEqualTo(33333);
-        assertThat(defaultFrameStyle.getFont())
-                .isEqualTo(Font.newBuilder().setSize(13).setItalic(true).build());
-        assertThat(defaultFrameStyle.getBackground())
-                .isEqualTo(Fill.newBuilder()
-                                   .setLinearGradient(
-                                           LinearGradient.newBuilder()
-                                                   .addStops(ColorStop.newBuilder().setColor(1234))
-                                                   .setDirectionDeg(321))
-                                   .build());
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-
-        // Frame's styles don't affect the childrens' styles.
-        assertThat(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .isEqualTo(mDefaultStyleProvider);
-        assertThat(frameContext.getDebugLogger().getMessages(MessageType.ERROR)).isEmpty();
-    }
-
-    @Test
-    public void testGetMediaQueryStylesheets() {
-        String noMediaQueryStylesheetId = "noMediaQueries";
-        Stylesheet noMediaQueryStylesheet =
-                Stylesheet.newBuilder().setStylesheetId(noMediaQueryStylesheetId).build();
-
-        String mediaQueryStylesheetId = "mediaQueries";
-        Stylesheet mediaQueryStylesheet =
-                Stylesheet.newBuilder()
-                        .setStylesheetId(mediaQueryStylesheetId)
-                        .addConditions(MediaQueryCondition.newBuilder().setFrameWidth(
-                                FrameWidthCondition.newBuilder().setWidth(0).setCondition(
-                                        ComparisonCondition.GREATER_THAN)))
-                        .build();
-
-        mPietSharedStates.add(PietSharedState.newBuilder()
-                                      .addStylesheets(mediaQueryStylesheet)
-                                      .addStylesheets(noMediaQueryStylesheet)
-                                      .build());
-
-        FrameContext frameContext = defaultFrameContext();
-
-        Template inlineStylesheetTemplate =
-                Template.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                Stylesheet.newBuilder().setStylesheetId("inline")))
-                        .build();
-        assertThat(frameContext.getMediaQueryStylesheets(inlineStylesheetTemplate)).isEmpty();
-
-        Template notFoundStylesheetTemplate =
-                Template.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheetIds("NotFound"))
-                        .build();
-        assertThat(frameContext.getMediaQueryStylesheets(notFoundStylesheetTemplate)).isEmpty();
-
-        Template noConditionsTemplate =
-                Template.newBuilder()
-                        .setStylesheets(
-                                Stylesheets.newBuilder().addStylesheetIds(noMediaQueryStylesheetId))
-                        .build();
-        assertThat(frameContext.getMediaQueryStylesheets(noConditionsTemplate)).isEmpty();
-
-        Template mediaQueryTemplate =
-                Template.newBuilder()
-                        .setStylesheets(
-                                Stylesheets.newBuilder().addStylesheetIds(mediaQueryStylesheetId))
-                        .build();
-        assertThat(frameContext.getMediaQueryStylesheets(mediaQueryTemplate))
-                .containsExactly(mediaQueryStylesheet);
-    }
-
-    @Test
-    public void testFilterImageSourcesByMediaQueryCondition() {
-        ImageSource activeSource =
-                ImageSource.newBuilder()
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.DARK)))
-                        .build();
-        ImageSource inactiveSource =
-                ImageSource.newBuilder()
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.LIGHT)))
-                        .build();
-        ImageSource sourceWithNoConditions = ImageSource.getDefaultInstance();
-        Image image = Image.newBuilder()
-                              .addSources(activeSource)
-                              .addSources(inactiveSource)
-                              .addSources(sourceWithNoConditions)
-                              .build();
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mFrameContext = defaultFrameContext();
-
-        Image resultImage = mFrameContext.filterImageSourcesByMediaQueryCondition(image);
-        assertThat(resultImage.getSourcesList())
-                .containsExactly(activeSource, sourceWithNoConditions);
-    }
-
-    private FrameContext defaultFrameContext() {
-        return makeFrameContextForDefaultFrame();
-    }
-
-    private FrameContext makeFrameContextForDefaultFrame() {
-        return new FrameContext(DEFAULT_FRAME, mDefaultStylesheet, mPietSharedStates,
-                newPietStylesHelper(), DebugBehavior.VERBOSE, mDebugLogger, mActionHandler,
-                mHostProviders, mFrameView);
-    }
-
-    private FrameContext makeFrameContextWithBinding(BindingValue bindingValue) {
-        Map<String, BindingValue> bindingValueMap = new HashMap<>();
-        bindingValueMap.put(bindingValue.getBindingId(), bindingValue);
-        return new FrameContext(DEFAULT_FRAME, mDefaultStylesheet, bindingValueMap,
-                mPietSharedStates, mPietStylesHelper, DebugBehavior.VERBOSE, mDebugLogger,
-                mActionHandler, mHostProviders, DEFAULT_TEMPLATES, mFrameView);
-    }
-
-    private FrameContext makeFrameContextWithNoBindings() {
-        Map<String, BindingValue> bindingValueMap = new HashMap<>();
-        bindingValueMap.put(BINDING_ID, BindingValue.getDefaultInstance());
-        return new FrameContext(DEFAULT_FRAME, mDefaultStylesheet, bindingValueMap,
-                mPietSharedStates, mPietStylesHelper, DebugBehavior.VERBOSE, mDebugLogger,
-                mActionHandler, mHostProviders, DEFAULT_TEMPLATES, mFrameView);
-    }
-
-    private FrameContext makeFrameContextFromFrame(Frame frame) {
-        return FrameContext.createFrameContext(frame, mPietSharedStates, newPietStylesHelper(),
-                DebugBehavior.VERBOSE, mDebugLogger, mActionHandler, mHostProviders, mFrameView);
-    }
-
-    private void setUpPietSharedStates() {
-        mPietSharedStates.add(PietSharedState.newBuilder()
-                                      .addStylesheets(Stylesheet.newBuilder()
-                                                              .setStylesheetId(STYLESHEET_ID)
-                                                              .addStyles(SAMPLE_STYLE))
-                                      .build());
-    }
-
-    private Frame getWorkingFrame() {
-        setUpPietSharedStates();
-        return getBaseFrame();
-    }
-
-    private Frame getBaseFrame() {
-        return Frame.newBuilder()
-                .setStylesheets(Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID))
-                .setStyleReferences(SAMPLE_STYLE_IDS)
-                .build();
-    }
-
-    private BindingValue.Builder defaultBinding() {
-        return BindingValue.newBuilder().setBindingId(BINDING_ID);
-    }
-
-    private PietStylesHelper newPietStylesHelper() {
-        return new PietStylesHelperFactory().get(
-                mPietSharedStates, new MediaQueryHelper(FRAME_WIDTH_PX, mAssetProvider, mContext));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/GridRowAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/GridRowAdapterTest.java
deleted file mode 100644
index e948381..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/GridRowAdapterTest.java
+++ /dev/null
@@ -1,980 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.library.piet.StyleProvider.DIMENSION_NOT_SET;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.Gravity;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.GridRowAdapter.KeySupplier;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.EventLogger;
-import org.chromium.chrome.browser.feed.library.piet.ui.GridRowView;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ElementBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.GridCellWidthBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementStack;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridCell;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridCellWidth;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridCellWidth.ContentWidth;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-
-/** Tests of the {@link GridRowAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class GridRowAdapterTest {
-    private static final String GRID_STYLE_ID = "cybercat";
-    private static final StyleIdsStack GRID_STYLES =
-            StyleIdsStack.newBuilder().addStyleIds(GRID_STYLE_ID).build();
-    private static final Element DEFAULT_ELEMENT =
-            Element.newBuilder().setElementStack(ElementStack.getDefaultInstance()).build();
-    private static final Content DEFAULT_CONTENT =
-            Content.newBuilder().setElement(DEFAULT_ELEMENT).build();
-    private static final Content TEXT_CONTENT =
-            Content.newBuilder()
-                    .setElement(Element.newBuilder().setTextElement(
-                            TextElement.newBuilder().setParameterizedText(
-                                    ParameterizedText.newBuilder().setText("TheGrid"))))
-                    .build();
-    private static final String BINDING_ID = "stripes";
-    private static final ElementBindingRef ELEMENT_BINDING =
-            ElementBindingRef.newBuilder().setBindingId(BINDING_ID).build();
-    private static final Content BOUND_CONTENTS =
-            Content.newBuilder().setBoundElement(ELEMENT_BINDING).build();
-    private static final Element GRID_ROW_WITH_BOUND_CELL =
-            Element.newBuilder()
-                    .setGridRow(GridRow.newBuilder().addCells(
-                            GridCell.newBuilder().setContent(BOUND_CONTENTS)))
-                    .build();
-
-    private Context mContext;
-    private AdapterParameters mAdapterParameters;
-
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private StyleProvider mStyleProvider;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private GridRowAdapter mAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mAssetProvider.isRtL()).thenReturn(false);
-        when(mAssetProvider.isRtLSupplier()).thenReturn(Suppliers.of(false));
-        when(mFrameContext.makeStyleFor(GRID_STYLES)).thenReturn(mStyleProvider);
-        when(mFrameContext.getActionHandler()).thenReturn(mActionHandler);
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
-                .thenAnswer(invocation -> invocation.getArguments()[0]);
-        when(mFrameContext.reportMessage(anyInt(), any(), anyString()))
-                .thenAnswer(invocation -> invocation.getArguments()[2]);
-        when(mStyleProvider.getPadding()).thenReturn(EdgeWidths.getDefaultInstance());
-        when(mStyleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
-
-        mAdapterParameters = new AdapterParameters(
-                mContext, Suppliers.of(null), mHostProviders, new FakeClock(), false, false);
-
-        when(mFrameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
-                .thenReturn(mAdapterParameters.mDefaultStyleProvider);
-
-        mAdapter = new KeySupplier().getAdapter(mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testViewDoesNotClip() {
-        assertThat(mAdapter.getBaseView().getClipToPadding()).isFalse();
-    }
-
-    @Test
-    public void testOnCreateAdapter_makesRow() {
-        // We create an adapter for the inline content, but not for the bound content.
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                                .addCells(GridCell.newBuilder().setContent(BOUND_CONTENTS))
-                                .build();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.getBaseView().getBaseline()).isEqualTo(-1);
-        assertThat(mAdapter.mChildAdapters).hasSize(1);
-        assertThat(mAdapter.getBaseView().getChildAt(0))
-                .isSameInstanceAs(mAdapter.mChildAdapters.get(0).getView());
-    }
-
-    @Test
-    public void testOnCreateAdapter_missingContentIsException() {
-        GridRow model = GridRow.newBuilder().addCells(GridCell.getDefaultInstance()).build();
-
-        assertThatRunnable(() -> mAdapter.createAdapter(asElement(model), mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unhandled Content type: CONTENTTYPE_NOT_SET");
-    }
-
-    @Test
-    public void testOnCreateAdapter_setsGridRowStyles() {
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                                .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-
-        verify(mFrameContext).makeStyleFor(GRID_STYLES);
-        verify(mStyleProvider).applyElementStyles(mAdapter);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_widthDefaultsToWeight() {
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT).clearWidth())
-                        .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.width).isEqualTo(0);
-        assertThat(params.weight).isEqualTo(1.0f);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_widthDp() {
-        int widthDp = 123;
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setContent(DEFAULT_CONTENT)
-                                          .setWidth(GridCellWidth.newBuilder().setDp(widthDp)))
-                        .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.width).isEqualTo((int) LayoutUtils.dpToPx(widthDp, mContext));
-        assertThat(params.weight).isEqualTo(0.0f);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_widthWeight() {
-        int widthWeight = 321;
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder()
-                                                  .setContent(DEFAULT_CONTENT)
-                                                  .setWidth(GridCellWidth.newBuilder().setWeight(
-                                                          widthWeight)))
-                                .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.weight).isEqualTo((float) widthWeight);
-        assertThat(params.width).isEqualTo(0);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_widthBinding() {
-        GridCellWidthBindingRef widthBindingRef =
-                GridCellWidthBindingRef.newBuilder().setBindingId("fatcat").build();
-        int widthDp = 222;
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder()
-                                                  .setContent(DEFAULT_CONTENT)
-                                                  .setWidthBinding(widthBindingRef))
-                                .build();
-        when(mFrameContext.getGridCellWidthFromBinding(widthBindingRef))
-                .thenReturn(GridCellWidth.newBuilder().setDp(widthDp).build());
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.width).isEqualTo((int) LayoutUtils.dpToPx(widthDp, mContext));
-        assertThat(params.weight).isEqualTo(0.0f);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_invalidContentWidth() {
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setContent(DEFAULT_CONTENT)
-                                          .setWidth(GridCellWidth.newBuilder().setContentWidth(
-                                                  ContentWidth.INVALID_CONTENT_WIDTH)))
-                        .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.width).isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(params.weight).isEqualTo(0.0f);
-        verify(mFrameContext)
-                .reportMessage(MessageType.WARNING, ErrorCode.ERR_GRID_CELL_WIDTH_WITHOUT_CONTENTS,
-                        "Invalid content width: INVALID_CONTENT_WIDTH");
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_widthOfChildAdapter() {
-        StyleIdsStack childStyles = StyleIdsStack.newBuilder().addStyleIds("child").build();
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(
-                                GridCell.newBuilder()
-                                        .setWidth(GridCellWidth.newBuilder().setContentWidth(
-                                                ContentWidth.CONTENT_WIDTH))
-                                        .setContent(Content.newBuilder().setElement(
-                                                // clang-format off
-                                    Element.newBuilder()
-                                            .setStyleReferences(childStyles)
-                                            .setImageElement(
-                                                    ImageElement.newBuilder().setImage(
-                                                            Image.getDefaultInstance()))
-                                                // clang-format on
-                                                )))
-                        .build();
-
-        StyleProvider childStyleProvider = mock(StyleProvider.class);
-        when(childStyleProvider.hasWidth()).thenReturn(true);
-        when(childStyleProvider.getWidthSpecPx(mContext)).thenReturn(456);
-        when(childStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-        when(childStyleProvider.getScaleType()).thenReturn(ImageView.ScaleType.CENTER_CROP);
-
-        when(mFrameContext.makeStyleFor(childStyles)).thenReturn(childStyleProvider);
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.width).isEqualTo((int) LayoutUtils.dpToPx(456, mContext));
-        assertThat(params.weight).isEqualTo(0.0f);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_heightOfChildAdapter() {
-        StyleIdsStack childStyles = StyleIdsStack.newBuilder().addStyleIds("child").build();
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(Content.newBuilder().setElement(
-                                Element.newBuilder()
-                                        .setStyleReferences(childStyles)
-                                        .setImageElement(ImageElement.newBuilder().setImage(
-                                                Image.getDefaultInstance())))))
-                        .build();
-
-        StyleProvider childStyleProvider = mock(StyleProvider.class);
-        when(childStyleProvider.hasHeight()).thenReturn(true);
-        when(childStyleProvider.getHeightSpecPx(mContext)).thenReturn(123);
-        when(childStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-        when(childStyleProvider.getScaleType()).thenReturn(ImageView.ScaleType.CENTER_CROP);
-
-        when(mFrameContext.makeStyleFor(childStyles)).thenReturn(childStyleProvider);
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.height).isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_heightOfChildAdapterNotDefined() {
-        StyleIdsStack childStyles = StyleIdsStack.newBuilder().addStyleIds("child").build();
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(Content.newBuilder().setElement(
-                                Element.newBuilder().setImageElement(
-                                        ImageElement.newBuilder().setImage(
-                                                Image.getDefaultInstance())))))
-                        .build();
-
-        StyleProvider childStyleProvider = mock(StyleProvider.class);
-        when(childStyleProvider.hasHeight()).thenReturn(false);
-        when(childStyleProvider.getHeightSpecPx(mContext)).thenReturn(DIMENSION_NOT_SET);
-        when(childStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-
-        when(mFrameContext.makeStyleFor(childStyles)).thenReturn(childStyleProvider);
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                .isInstanceOf(LinearLayout.LayoutParams.class);
-        LayoutParams params =
-                (LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams();
-        assertThat(params.height).isEqualTo(LayoutParams.MATCH_PARENT);
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_margins() {
-        StyleIdsStack childStyles = StyleIdsStack.newBuilder().addStyleIds("child").build();
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(Content.newBuilder().setElement(
-                                Element.newBuilder()
-                                        .setStyleReferences(childStyles)
-                                        .setElementStack(ElementStack.getDefaultInstance()))))
-                        .build();
-
-        StyleProvider childStyleProvider = mock(StyleProvider.class);
-        when(childStyleProvider.getPadding()).thenReturn(EdgeWidths.getDefaultInstance());
-        when(childStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-
-        when(mFrameContext.makeStyleFor(childStyles)).thenReturn(childStyleProvider);
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        verify(childStyleProvider)
-                .applyMargins(mContext,
-                        (MarginLayoutParams) mAdapter.getBaseView()
-                                .getChildAt(0)
-                                .getLayoutParams());
-    }
-
-    @Test
-    public void testOnBindModel_setsLayoutParamsOnCell_verticalGravityCenter() {
-        StyleIdsStack centerVertical =
-                StyleIdsStack.newBuilder().addStyleIds("center_vertical").build();
-        StyleProvider centerVerticalProvider = mock(StyleProvider.class);
-        when(mFrameContext.makeStyleFor(centerVertical)).thenReturn(centerVerticalProvider);
-        when(centerVerticalProvider.getGravityVertical(anyInt()))
-                .thenReturn(Gravity.CENTER_VERTICAL);
-        GridRow gridRowTop =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(Content.newBuilder().setElement(
-                                DEFAULT_ELEMENT.toBuilder().setStyleReferences(centerVertical))))
-                        .build();
-
-        mAdapter.createAdapter(asElement(gridRowTop), mFrameContext);
-        mAdapter.bindModel(asElement(gridRowTop), mFrameContext);
-
-        ElementStackAdapter cellAdapter = (ElementStackAdapter) mAdapter.mChildAdapters.get(0);
-        LayoutParams params = (LinearLayout.LayoutParams) cellAdapter.getView().getLayoutParams();
-        assertThat(params.gravity).isEqualTo(Gravity.CENTER_VERTICAL);
-    }
-
-    @Test
-    public void testOnBindModel_collapsibleCells_valid() {
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setContentWidth(
-                                                  ContentWidth.CONTENT_WIDTH))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(gridRow), mFrameContext);
-        mAdapter.bindModel(asElement(gridRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(mAdapter.getBaseView().getChildAt(1).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(1).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(2).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testOnBindModel_collapsibleCells_multipleCollapsible() {
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setDp(456)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(gridRow), mFrameContext);
-        mAdapter.bindModel(asElement(gridRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(1).getLayoutParams().width).isEqualTo(456);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(1).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(2).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testOnBindModel_collapsibleCells_mixingCollapsibleAndWeight() {
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setWeight(4))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(gridRow), mFrameContext);
-        mAdapter.bindModel(asElement(gridRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(1).getLayoutParams().width).isEqualTo(0);
-        assertThat(
-                ((LinearLayout.LayoutParams) mAdapter.getBaseView().getChildAt(1).getLayoutParams())
-                        .weight)
-                .isEqualTo(4.0f);
-        assertThat(mAdapter.getBaseView().getChildAt(2).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testOnBindModel_recreatesBindingCells() {
-        Element cellWithOneElement =
-                Element.newBuilder()
-                        .setElementList(ElementList.newBuilder().addContents(
-                                Content.newBuilder().setElement(
-                                        Element.newBuilder().setElementStack(
-                                                ElementStack.getDefaultInstance()))))
-                        .build();
-        Element cellWithTwoElements =
-                Element.newBuilder()
-                        .setElementList(
-                                ElementList.newBuilder()
-                                        .addContents(Content.newBuilder().setElement(
-                                                Element.newBuilder().setElementStack(
-                                                        ElementStack.getDefaultInstance())))
-                                        .addContents(Content.newBuilder().setElement(
-                                                Element.newBuilder().setElementStack(
-                                                        ElementStack.getDefaultInstance()))))
-                        .build();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(cellWithOneElement).build());
-        mAdapter.createAdapter(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-        // The cell adapter has not been created yet
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(cellWithTwoElements).build());
-        mAdapter.bindModel(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-        // The cell adapter creates its one view on bind.
-        assertThat(((LinearLayout) mAdapter.getBaseView().getChildAt(0)).getChildCount())
-                .isEqualTo(2);
-
-        mAdapter.unbindModel();
-        // The cell adapter has been released.
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(cellWithOneElement).build());
-        mAdapter.bindModel(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-        // The cell adapter can bind to a different model.
-        assertThat(((LinearLayout) mAdapter.getBaseView().getChildAt(0)).getChildCount())
-                .isEqualTo(1);
-    }
-
-    @Test
-    public void testOnBindModel_visibilityGone() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(BINDING_ID)
-                                    .setElement(DEFAULT_ELEMENT)
-                                    .build());
-        mAdapter.createAdapter(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(BINDING_ID)
-                                    .setVisibility(Visibility.GONE)
-                                    .build());
-
-        mAdapter.bindModel(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_noContent() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(BINDING_ID)
-                                    .setElement(DEFAULT_ELEMENT)
-                                    .build());
-        mAdapter.createAdapter(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setBindingId(BINDING_ID).build());
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_optionalAbsent() {
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(BINDING_ID)
-                                    .setElement(DEFAULT_ELEMENT)
-                                    .build());
-        mAdapter.createAdapter(GRID_ROW_WITH_BOUND_CELL, mFrameContext);
-
-        ElementBindingRef optionalBinding = ELEMENT_BINDING.toBuilder().setIsOptional(true).build();
-        GridRow optionalBindingRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(
-                                Content.newBuilder().setBoundElement(optionalBinding)))
-                        .build();
-        when(mFrameContext.getElementBindingValue(optionalBinding))
-                .thenReturn(BindingValue.newBuilder().setBindingId(BINDING_ID).build());
-
-        mAdapter.bindModel(asElement(optionalBindingRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testOnBindModel_throwsExceptionOnCellCountMismatch() {
-        GridRow gridRowWithTwoElements =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                        .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                        .build();
-
-        GridRow gridRowWithOneElement =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(gridRowWithTwoElements), mFrameContext);
-
-        assertThatRunnable(
-                () -> mAdapter.bindModel(asElement(gridRowWithOneElement), mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Internal error in adapters per content");
-    }
-
-    @Test
-    public void testOnBindModel_setsStylesOnlyIfBindingIsDefined() {
-        GridRow gridRow = GridRow.newBuilder()
-                                  .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                                  .build();
-
-        mAdapter.createAdapter(asElement(gridRow, GRID_STYLES), mFrameContext);
-        verify(mFrameContext).makeStyleFor(GRID_STYLES);
-
-        // When we bind a new model, the style does not change.
-        StyleIdsStack otherStyles = StyleIdsStack.newBuilder().addStyleIds("ignored").build();
-
-        mAdapter.bindModel(asElement(gridRow, otherStyles), mFrameContext);
-        verify(mFrameContext, never()).makeStyleFor(otherStyles);
-
-        // If we bind a model that has a style binding, then the style does get re-applied.
-        StyleIdsStack styleWithBinding =
-                StyleIdsStack.newBuilder()
-                        .setStyleBinding(StyleBindingRef.newBuilder().setBindingId("homewardbound"))
-                        .build();
-        StyleProvider otherStyleProvider = mock(StyleProvider.class);
-        when(mFrameContext.makeStyleFor(styleWithBinding)).thenReturn(otherStyleProvider);
-
-        mAdapter.bindModel(asElement(gridRow, styleWithBinding), mFrameContext);
-        verify(mFrameContext).makeStyleFor(styleWithBinding);
-        verify(otherStyleProvider).applyElementStyles(mAdapter);
-    }
-
-    @Test
-    public void testOnBindModel_collapsibleCells_valid_boundWidth() {
-        GridCellWidthBindingRef widthBinding =
-                GridCellWidthBindingRef.newBuilder().setBindingId("width").build();
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setContentWidth(
-                                                  ContentWidth.CONTENT_WIDTH))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidthBinding(widthBinding)
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        when(mFrameContext.getGridCellWidthFromBinding(widthBinding))
-                .thenReturn(GridCellWidth.newBuilder().setDp(456).build());
-        mAdapter.createAdapter(asElement(gridRow), mFrameContext);
-
-        when(mFrameContext.getGridCellWidthFromBinding(widthBinding))
-                .thenReturn(GridCellWidth.newBuilder()
-                                    .setContentWidth(ContentWidth.CONTENT_WIDTH)
-                                    .setIsCollapsible(true)
-                                    .build());
-        mAdapter.bindModel(asElement(gridRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(mAdapter.getBaseView().getChildAt(1).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(1).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(2).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testOnBindModel_collapsibleCells_multipleCollapsible_boundWidth() {
-        GridCellWidthBindingRef widthBinding =
-                GridCellWidthBindingRef.newBuilder().setBindingId("width").build();
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidthBinding(widthBinding)
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        when(mFrameContext.getGridCellWidthFromBinding(widthBinding))
-                .thenReturn(GridCellWidth.newBuilder().setDp(456).build());
-        mAdapter.createAdapter(asElement(gridRow), mFrameContext);
-
-        when(mFrameContext.getGridCellWidthFromBinding(widthBinding))
-                .thenReturn(GridCellWidth.newBuilder()
-                                    .setContentWidth(ContentWidth.CONTENT_WIDTH)
-                                    .setIsCollapsible(true)
-                                    .build());
-        mAdapter.bindModel(asElement(gridRow), mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getChildAt(0).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(0).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(1).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                ((GridRowView.LayoutParams) mAdapter.getBaseView().getChildAt(1).getLayoutParams())
-                        .getIsCollapsible())
-                .isTrue();
-        assertThat(mAdapter.getBaseView().getChildAt(2).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-    }
-
-    @Test
-    public void testUnbindModel() {
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                                .addCells(GridCell.newBuilder().setContent(
-                                        Content.newBuilder().setBoundElement(ELEMENT_BINDING)))
-                                .build();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        mAdapter.unbindModel();
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(1);
-        assertThat(mAdapter.mChildAdapters).hasSize(1);
-
-        // The inline adapter has been unbound.
-        assertThat(mAdapter.mChildAdapters.get(0).getRawModel()).isNull();
-    }
-
-    @Test
-    public void testUnbindModel_worksTwice() {
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        mAdapter.unbindModel();
-        mAdapter.unbindModel();
-
-        // assert no failure.
-    }
-
-    @Test
-    public void testReleaseAdapter() {
-        GridRow model = GridRow.newBuilder()
-                                .addCells(GridCell.newBuilder().setContent(DEFAULT_CONTENT))
-                                .addCells(GridCell.newBuilder().setContent(
-                                        Content.newBuilder().setBoundElement(ELEMENT_BINDING)))
-                                .build();
-
-        when(mFrameContext.getElementBindingValue(ELEMENT_BINDING))
-                .thenReturn(BindingValue.newBuilder().setElement(DEFAULT_ELEMENT).build());
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-        assertThat(mAdapter.getBaseView().getChildCount()).isEqualTo(0);
-        assertThat(mAdapter.mChildAdapters).isEmpty();
-    }
-
-    @Test
-    public void testReleaseAdapter_collapsibleCells() {
-        GridRow model =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-
-        mAdapter.createAdapter(asElement(model), mFrameContext);
-        mAdapter.bindModel(asElement(model), mFrameContext);
-
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        assertThat(mAdapter.getBaseView().hasCollapsibleCells()).isFalse();
-    }
-
-    /**
-     * Mini integration test to ensure that WRAP_CONTENT is set on GridRowAdapter when it is within
-     * a hierarchy
-     */
-    @Test
-    public void testCollapsibleCells_integration() {
-        GridRow gridRow =
-                GridRow.newBuilder()
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setDp(123))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder()
-                                                            .setContentWidth(
-                                                                    ContentWidth.CONTENT_WIDTH)
-                                                            .setIsCollapsible(true))
-                                          .setContent(TEXT_CONTENT))
-                        .addCells(GridCell.newBuilder()
-                                          .setWidth(GridCellWidth.newBuilder().setContentWidth(
-                                                  ContentWidth.CONTENT_WIDTH))
-                                          .setContent(TEXT_CONTENT))
-                        .build();
-        Frame frame = Frame.newBuilder()
-                              .addContents(Content.newBuilder().setElement(
-                                      Element.newBuilder().setElementList(
-                                              ElementList.newBuilder().addContents(
-                                                      Content.newBuilder().setElement(
-                                                              Element.newBuilder().setGridRow(
-                                                                      gridRow))))))
-                              .build();
-        EventLogger mockEventLogger = mock(EventLogger.class);
-        FrameAdapterImpl frameAdapter = new FrameAdapterImpl(mContext, mAdapterParameters,
-                mActionHandler, mockEventLogger, DebugBehavior.SILENT);
-        frameAdapter.bindModel(frame, 0, null, Collections.emptyList());
-        LinearLayout gridRowView =
-                ((LinearLayout) ((LinearLayout) frameAdapter.getView().getChildAt(0))
-                                .getChildAt(0));
-        assertThat(gridRowView.getChildCount()).isEqualTo(3);
-        assertThat(gridRowView.getChildAt(0).getLayoutParams().width)
-                .isEqualTo((int) LayoutUtils.dpToPx(123, mContext));
-        assertThat(((GridRowView.LayoutParams) gridRowView.getChildAt(1).getLayoutParams())
-                           .getIsCollapsible())
-                .isTrue();
-        assertThat(gridRowView.getChildAt(2).getLayoutParams().width)
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-    }
-
-    @Test
-    public void testGetStyleIdsStack() {
-        mAdapter.createAdapter(asElement(GridRow.getDefaultInstance(), GRID_STYLES), mFrameContext);
-        assertThat(mAdapter.getElementStyleIdsStack()).isEqualTo(GRID_STYLES);
-    }
-
-    @Test
-    public void testCreateViewGroup() {
-        LinearLayout gridView = GridRowAdapter.createView(mContext, Suppliers.of(false));
-        assertThat(gridView.getOrientation()).isEqualTo(LinearLayout.HORIZONTAL);
-        assertThat(gridView.getLayoutParams().width).isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(gridView.getLayoutParams().height).isEqualTo(LayoutParams.WRAP_CONTENT);
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        GridRow model = GridRow.newBuilder().build();
-
-        Element elementWithModel =
-                Element.newBuilder()
-                        .setStyleReferences(StyleIdsStack.newBuilder().addStyleIds("spacer"))
-                        .setGridRow(model)
-                        .build();
-        assertThat(mAdapter.getModelFromElement(elementWithModel)).isSameInstanceAs(model);
-
-        Element elementWithWrongModel =
-                Element.newBuilder().setCustomElement(CustomElement.getDefaultInstance()).build();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(elementWithWrongModel))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing GridRow");
-
-        Element emptyElement = Element.getDefaultInstance();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(emptyElement))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing GridRow");
-    }
-
-    private static Element asElement(GridRow gridRow, StyleIdsStack styles) {
-        return Element.newBuilder().setStyleReferences(styles).setGridRow(gridRow).build();
-    }
-
-    private static Element asElement(GridRow gridRow) {
-        return Element.newBuilder().setStyleReferences(GRID_STYLES).setGridRow(gridRow).build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ImageElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ImageElementAdapterTest.java
deleted file mode 100644
index 7760665..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ImageElementAdapterTest.java
+++ /dev/null
@@ -1,612 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.api.host.imageloader.ImageLoaderApi.DIMENSION_UNKNOWN;
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.library.piet.StyleProvider.DIMENSION_NOT_SET;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerWrapperView;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ImageBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ImageElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Visibility;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.ImageSource;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition.DarkLightMode;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.MediaQueryCondition;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ImageElementAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ImageElementAdapterTest {
-    private static final int HEIGHT_DP = 123;
-    private static final int WIDTH_DP = 321;
-    private static final EdgeWidths PADDING =
-            EdgeWidths.newBuilder().setBottom(1).setTop(2).setStart(3).setEnd(4).build();
-    private static final RoundedCorners CORNERS = RoundedCorners.newBuilder()
-                                                          .setBitmask(Corners.BOTTOM_START_VALUE)
-                                                          .setRadiusDp(34)
-                                                          .build();
-    private static final Image DEFAULT_IMAGE =
-            Image.newBuilder().addSources(ImageSource.newBuilder().setUrl("icanhas.chz")).build();
-    private static final Element DEFAULT_MODEL =
-            asElement(ImageElement.newBuilder().setImage(DEFAULT_IMAGE).build());
-    private static final boolean LEGACY_CORNERS_FLAG = false;
-    private static final boolean OUTLINE_CORNERS_FLAG = false;
-
-    @Mock
-    private ElementAdapterFactory mAdapterFactory;
-    @Mock
-    private TemplateBinder mTemplateBinder;
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private AssetProvider mAssetProvider;
-    @Mock
-    private StyleProvider mStyleProvider;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private LoadImageCallback mLoadImageCallback;
-
-    private Context mContext;
-    private int mHeightPx;
-    private int mWidthPx;
-    private ImageView mImageView;
-    private final FakeClock mClock = new FakeClock();
-    private RoundedCornerMaskCache mMaskCache;
-
-    private ImageElementAdapterForTest mAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mHeightPx = (int) LayoutUtils.dpToPx(HEIGHT_DP, mContext);
-        mWidthPx = (int) LayoutUtils.dpToPx(WIDTH_DP, mContext);
-        mMaskCache = new RoundedCornerMaskCache();
-        AdapterParameters parameters = new AdapterParameters(mContext, null, mHostProviders, null,
-                mAdapterFactory, mTemplateBinder, mClock, new PietStylesHelperFactory(), mMaskCache,
-                LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG);
-
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(mStyleProvider);
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
-                .thenAnswer(invocation -> invocation.getArguments()[0]);
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mStyleProvider.getPadding()).thenReturn(PADDING);
-        when(mStyleProvider.hasRoundedCorners()).thenReturn(true);
-        when(mStyleProvider.getRoundedCorners()).thenReturn(CORNERS);
-        when(mStyleProvider.getScaleType()).thenReturn(ScaleType.FIT_CENTER);
-        when(mStyleProvider.createWrapperView(
-                     mContext, mMaskCache, LEGACY_CORNERS_FLAG, OUTLINE_CORNERS_FLAG))
-                .thenReturn(new RoundedCornerWrapperView(mContext, CORNERS, mMaskCache,
-                        Suppliers.of(false),
-                        /*radiusOverride= */ 0,
-                        /* borders= */ null,
-                        /* allowClipPath= */ false,
-                        /* allowOutlineRounding= */ false));
-        setStyle(null, null);
-
-        mAdapter = new ImageElementAdapterForTest(mContext, parameters);
-    }
-
-    @Test
-    public void testCreate() {
-        assertThat(mAdapter).isNotNull();
-    }
-
-    @Test
-    public void testCreateAdapter() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        assertThat(mAdapter.getModel()).isSameInstanceAs(DEFAULT_MODEL.getImageElement());
-
-        assertThat(mAdapter.getView()).isNotNull();
-
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(mHeightPx);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(mWidthPx);
-        assertThat(mAdapter.getBaseView().getCropToPadding()).isTrue();
-        verify(mStyleProvider).applyElementStyles(mAdapter);
-    }
-
-    @Test
-    public void testCreateAdapter_noDimensionsSet() {
-        setStyle(null, null);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getModel()).isSameInstanceAs(DEFAULT_MODEL.getImageElement());
-
-        assertThat(mAdapter.getView()).isNotNull();
-
-        // Assert that width and height are set to the defaults
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testCreateAdapter_heightOnly() {
-        setStyle(HEIGHT_DP, null);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getModel()).isEqualTo(DEFAULT_MODEL.getImageElement());
-
-        assertThat(mAdapter.getView()).isNotNull();
-
-        // Width defaults to MATCH_PARENT
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(mHeightPx);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testCreateAdapter_widthOnly() {
-        setStyle(null, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getModel()).isEqualTo(DEFAULT_MODEL.getImageElement());
-
-        assertThat(mAdapter.getView()).isNotNull();
-
-        // Image defaults to a square.
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(mWidthPx);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(mWidthPx);
-    }
-
-    @Test
-    public void testCreateAdapter_noContent() {
-        Element model = asElement(ImageElement.getDefaultInstance());
-
-        mAdapter.createAdapter(model, mFrameContext);
-
-        assertThatRunnable(() -> mAdapter.bindModel(model, mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unsupported or missing content");
-    }
-
-    @Test
-    public void testBindModel_image() {
-        StyleIdsStack styles = StyleIdsStack.newBuilder().addStyleIds("stylecat").build();
-        Element model = Element.newBuilder()
-                                .setStyleReferences(styles)
-                                .setImageElement(ImageElement.newBuilder().setImage(DEFAULT_IMAGE))
-                                .build();
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        mImageView = mAdapter.getBaseView();
-        verify(mAssetProvider)
-                .getImage(DEFAULT_IMAGE, DIMENSION_UNKNOWN, DIMENSION_UNKNOWN, mLoadImageCallback);
-
-        assertThat(mAdapter.getModel()).isSameInstanceAs(model.getImageElement());
-        assertThat(mAdapter.getElementStyleIdsStack()).isEqualTo(styles);
-    }
-
-    @Test
-    public void testBindModel_imageBinding() {
-        ImageBindingRef imageBinding = ImageBindingRef.newBuilder().setBindingId("feline").build();
-        Element model = asElement(ImageElement.newBuilder().setImageBinding(imageBinding).build());
-        when(mFrameContext.getImageBindingValue(imageBinding))
-                .thenReturn(BindingValue.newBuilder().setImage(DEFAULT_IMAGE).build());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        verify(mAssetProvider)
-                .getImage(DEFAULT_IMAGE, DIMENSION_UNKNOWN, DIMENSION_UNKNOWN, mLoadImageCallback);
-        assertThat(mAdapter.getModel()).isSameInstanceAs(model.getImageElement());
-    }
-
-    @Test
-    public void testBindModel_optionalAbsent() {
-        String bindingRef = "foto";
-        ImageBindingRef imageBindingRef =
-                ImageBindingRef.newBuilder().setBindingId(bindingRef).setIsOptional(true).build();
-        Element imageBindingElement =
-                asElement(ImageElement.newBuilder().setImageBinding(imageBindingRef).build());
-        mAdapter.createAdapter(
-                asElement(ImageElement.newBuilder().setImage(Image.getDefaultInstance()).build()),
-                mFrameContext);
-        when(mFrameContext.getImageBindingValue(imageBindingRef))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.bindModel(imageBindingElement, mFrameContext);
-        assertThat(mAdapter.getBaseView().getDrawable()).isNull();
-        assertThat(mAdapter.getBaseView().getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void testBindModel_noContentInBindingValue() {
-        String bindingRef = "foto";
-        ImageBindingRef imageBindingRef =
-                ImageBindingRef.newBuilder().setBindingId(bindingRef).build();
-        Element imageBindingElement =
-                asElement(ImageElement.newBuilder().setImageBinding(imageBindingRef).build());
-        mAdapter.createAdapter(
-                asElement(ImageElement.newBuilder().setImage(Image.getDefaultInstance()).build()),
-                mFrameContext);
-        when(mFrameContext.getImageBindingValue(imageBindingRef))
-                .thenReturn(BindingValue.newBuilder()
-                                    .setBindingId(bindingRef)
-                                    .setVisibility(Visibility.VISIBLE)
-                                    .clearImage()
-                                    .build());
-
-        assertThatRunnable(() -> mAdapter.bindModel(imageBindingElement, mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Image binding foto had no content");
-    }
-
-    @Test
-    public void testBindModel_setsScaleType() {
-        StyleIdsStack styles = StyleIdsStack.newBuilder().addStyleIds("stylecat").build();
-        when(mStyleProvider.getScaleType()).thenReturn(ImageView.ScaleType.CENTER_CROP);
-        Element model = asElement(ImageElement.newBuilder()
-                                          .setImage(DEFAULT_IMAGE)
-                                          .setStyleReferences(styles)
-                                          .build());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        verify(mAssetProvider)
-                .getImage(DEFAULT_IMAGE, DIMENSION_UNKNOWN, DIMENSION_UNKNOWN, mLoadImageCallback);
-        assertThat(mAdapter.mScaleTypeForCallback).isEqualTo(ScaleType.CENTER_CROP);
-    }
-
-    @Test
-    public void testBindModel_again() {
-        // Bind a model, then unbind it.
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-        mImageView = mAdapter.getBaseView();
-        RecyclerKey key1 = mAdapter.getKey();
-        mAdapter.unbindModel();
-
-        // Bind a different model
-        Element model2 =
-                asElement(ImageElement.newBuilder().setImage(Image.getDefaultInstance()).build());
-        mAdapter.bindModel(model2, mFrameContext);
-        verify(mAssetProvider)
-                .getImage(Image.getDefaultInstance(), WIDTH_DP, HEIGHT_DP, mLoadImageCallback);
-
-        RecyclerKey key2 = mAdapter.getKey();
-        assertThat(key1).isSameInstanceAs(key2);
-        assertThat(mAdapter.getModel()).isSameInstanceAs(model2.getImageElement());
-        assertThat(mAdapter.getView()).isNotNull();
-
-        ImageView imageView2 = mAdapter.getBaseView();
-
-        assertThat(imageView2).isSameInstanceAs(mImageView);
-    }
-
-    @Test
-    public void testBindModel_bindingTwiceThrowsException() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-
-        assertThatRunnable(() -> mAdapter.bindModel(DEFAULT_MODEL, mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("An image loading callback exists");
-    }
-
-    @Test
-    public void testBindModel_setsStylesOnlyIfBindingIsDefined() {
-        // Create an adapter with a default style
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        verify(mStyleProvider).applyElementStyles(mAdapter);
-
-        // Styles do not change when a different model is bound
-        StyleIdsStack otherStyle = StyleIdsStack.newBuilder().addStyleIds("ignored").build();
-        Element imageWithOtherStyle =
-                DEFAULT_MODEL.toBuilder().setStyleReferences(otherStyle).build();
-        mAdapter.bindModel(imageWithOtherStyle, mFrameContext);
-        mAdapter.unbindModel();
-
-        verify(mFrameContext, never()).makeStyleFor(otherStyle);
-
-        // Styles do change when a model with a style binding is bound
-        StyleIdsStack boundStyle =
-                StyleIdsStack.newBuilder()
-                        .setStyleBinding(StyleBindingRef.newBuilder().setBindingId("tuna"))
-                        .build();
-        Element imageWithBoundStyle =
-                DEFAULT_MODEL.toBuilder().setStyleReferences(boundStyle).build();
-        mAdapter.bindModel(imageWithBoundStyle, mFrameContext);
-
-        verify(mFrameContext).makeStyleFor(boundStyle);
-
-        verify(mStyleProvider, times(2)).applyElementStyles(mAdapter);
-    }
-
-    @Test
-    public void testBindModel_preLoadFill() {
-        Drawable preLoadFillDrawable = new ColorDrawable(Color.RED);
-
-        // Set up the StyleProvider mock
-        when(mStyleProvider.createPreLoadFill()).thenReturn(preLoadFillDrawable);
-        when(mStyleProvider.hasPreLoadFill()).thenReturn(true);
-
-        // Bind and expect the pre-load fill to be set
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-        assertThat(mAdapter.getBaseView().getDrawable()).isSameInstanceAs(preLoadFillDrawable);
-
-        // Load drawable and replace pre-load fill
-        verify(mAssetProvider)
-                .getImage(DEFAULT_IMAGE, DIMENSION_UNKNOWN, DIMENSION_UNKNOWN, mLoadImageCallback);
-    }
-
-    @Test
-    public void testBindModel_color() {
-        int red = 0xFFFF0000;
-        ImageElement defaultImageElement =
-                ImageElement.newBuilder().setImage(DEFAULT_IMAGE).build();
-        StyleProvider redTintStyleProvider = new StyleProvider(
-                Style.newBuilder().setStyleId("red").setColor(red).build(), mAssetProvider);
-        StyleIdsStack redTintStyle = StyleIdsStack.newBuilder().addStyleIds("red").build();
-        when(mFrameContext.makeStyleFor(redTintStyle)).thenReturn(redTintStyleProvider);
-
-        Element modelWithOverlayColor = Element.newBuilder()
-                                                .setStyleReferences(redTintStyle)
-                                                .setImageElement(defaultImageElement)
-                                                .build();
-
-        // Bind and expect tint to be set
-        mAdapter.createAdapter(modelWithOverlayColor, mFrameContext);
-        mAdapter.bindModel(modelWithOverlayColor, mFrameContext);
-        verify(mFrameContext).makeStyleFor(redTintStyle);
-        assertThat(mAdapter.mOverlayColorForCallback).isEqualTo(red);
-    }
-
-    @Test
-    public void testBindModel_filtersImageSources() {
-        ImageSource activeSource =
-                ImageSource.newBuilder()
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.DARK)))
-                        .build();
-        ImageSource inactiveSource =
-                ImageSource.newBuilder()
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.LIGHT)))
-                        .build();
-        Image image =
-                Image.newBuilder().addSources(activeSource).addSources(inactiveSource).build();
-        Image filteredImage = Image.newBuilder().addSources(activeSource).build();
-        when(mFrameContext.filterImageSourcesByMediaQueryCondition(image))
-                .thenReturn(filteredImage);
-
-        Element model = asElement(ImageElement.newBuilder().setImage(image).build());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        verify(mAssetProvider)
-                .getImage(eq(filteredImage), anyInt(), anyInt(), any(LoadImageCallback.class));
-    }
-
-    @Test
-    public void testBindModel_setsAspectRatio() {
-        StyleIdsStack styles = StyleIdsStack.newBuilder().addStyleIds("stylecat").build();
-        setStyle(null, null);
-        Element model = asElement(ImageElement.newBuilder()
-                                          .setImage(Image.newBuilder().addSources(
-                                                  ImageSource.newBuilder()
-                                                          .setWidthPx(100)
-                                                          .setHeightPx(20) // Aspect ratio of 5.0
-                                                          .setUrl("http://whatever")))
-                                          .setStyleReferences(styles)
-                                          .build());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-
-        mImageView = mAdapter.getBaseView();
-
-        mImageView.measure(MeasureSpec.makeMeasureSpec(10, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-
-        assertThat(mImageView.getMeasuredWidth()).isEqualTo(10);
-        assertThat(mImageView.getMeasuredHeight()).isEqualTo(2);
-    }
-
-    @Test
-    public void testUnbind() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-        mAdapter.unbindModel();
-
-        assertThat(mAdapter.getView()).isNotNull();
-        assertThat(mAdapter.getBaseView().getDrawable()).isNull();
-
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(mHeightPx);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(mWidthPx);
-    }
-
-    @Test
-    public void testUnbind_cancelsCallback() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-
-        mImageView = mAdapter.getBaseView();
-
-        mAdapter.unbindModel();
-        verify(mAssetProvider).getImage(DEFAULT_IMAGE, WIDTH_DP, HEIGHT_DP, mLoadImageCallback);
-        verify(mLoadImageCallback).cancel();
-    }
-
-    @Test
-    public void testReleaseAdapter_resetsDims() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-        mAdapter.bindModel(DEFAULT_MODEL, mFrameContext);
-        mAdapter.unbindModel();
-        mAdapter.releaseAdapter();
-
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testComputedDimensions_unbound() {
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testComputedDimensions_bound() {
-        setStyle(HEIGHT_DP, WIDTH_DP);
-        mAdapter.createAdapter(DEFAULT_MODEL, mFrameContext);
-
-        assertThat(mAdapter.getComputedHeightPx()).isEqualTo(mHeightPx);
-        assertThat(mAdapter.getComputedWidthPx()).isEqualTo(mWidthPx);
-    }
-
-    @Test
-    public void testGetAspectRatio_succeeds() {
-        Image image = Image.newBuilder()
-                              .addSources(ImageSource.getDefaultInstance())
-                              .addSources(ImageSource.newBuilder().setHeightPx(123))
-                              .addSources(ImageSource.newBuilder().setWidthPx(456))
-                              .addSources(ImageSource.newBuilder().setWidthPx(99).setHeightPx(
-                                      33)) // This one gets picked
-                              .addSources(ImageSource.newBuilder().setWidthPx(100).setHeightPx(50))
-                              .build();
-        assertThat(ImageElementAdapter.getAspectRatio(image)).isWithin(0.01f).of(3.0f);
-    }
-
-    @Test
-    public void testGetAspectRatio_fails() {
-        Image image = Image.newBuilder()
-                              .addSources(ImageSource.getDefaultInstance())
-                              .addSources(ImageSource.newBuilder().setHeightPx(123))
-                              .addSources(ImageSource.newBuilder().setWidthPx(456))
-                              .build();
-        assertThat(ImageElementAdapter.getAspectRatio(image)).isZero();
-        assertThat(ImageElementAdapter.getAspectRatio(Image.getDefaultInstance())).isZero();
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        ImageElement model =
-                ImageElement.newBuilder()
-                        .setStyleReferences(StyleIdsStack.newBuilder().addStyleIds("image"))
-                        .build();
-
-        Element elementWithModel = Element.newBuilder().setImageElement(model).build();
-        assertThat(mAdapter.getModelFromElement(elementWithModel)).isSameInstanceAs(model);
-
-        Element elementWithWrongModel =
-                Element.newBuilder().setCustomElement(CustomElement.getDefaultInstance()).build();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(elementWithWrongModel))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing ImageElement");
-
-        Element emptyElement = Element.getDefaultInstance();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(emptyElement))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing ImageElement");
-    }
-
-    private void setStyle(/*@Nullable*/ Integer height, /*@Nullable*/ Integer width) {
-        if (height != null) {
-            when(mStyleProvider.hasHeight()).thenReturn(true);
-            when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(height);
-        } else {
-            when(mStyleProvider.hasHeight()).thenReturn(false);
-            when(mStyleProvider.getHeightSpecPx(mContext)).thenReturn(DIMENSION_NOT_SET);
-        }
-        if (width != null) {
-            when(mStyleProvider.hasWidth()).thenReturn(true);
-            when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(width);
-        } else {
-            when(mStyleProvider.hasWidth()).thenReturn(false);
-            when(mStyleProvider.getWidthSpecPx(mContext)).thenReturn(DIMENSION_NOT_SET);
-        }
-    }
-
-    private static Element asElement(ImageElement imageElement) {
-        return Element.newBuilder().setImageElement(imageElement).build();
-    }
-
-    private class ImageElementAdapterForTest extends ImageElementAdapter {
-        private ScaleType mScaleTypeForCallback;
-        private Integer mOverlayColorForCallback;
-
-        private ImageElementAdapterForTest(Context context, AdapterParameters parameters) {
-            super(context, parameters);
-        }
-
-        @Override
-        LoadImageCallback createLoadImageCallback(ScaleType scaleType,
-                /*@Nullable*/ Integer overlayColor, FrameContext frameContext) {
-            this.mScaleTypeForCallback = scaleType;
-            this.mOverlayColorForCallback = overlayColor;
-            return mLoadImageCallback;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/KeyedRecyclerPoolTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/KeyedRecyclerPoolTest.java
deleted file mode 100644
index 50117fd1..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/KeyedRecyclerPoolTest.java
+++ /dev/null
@@ -1,196 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link KeyedRecyclerPool} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class KeyedRecyclerPoolTest {
-    private static final RecyclerKey KEY1 = new TestRecyclerKey("KEY1");
-    private static final RecyclerKey KEY2 = new TestRecyclerKey("KEY2");
-    private static final RecyclerKey KEY3 = new TestRecyclerKey("KEY3");
-    private static final int MAX_KEYS = 10;
-    private static final int CAPACITY = 11;
-
-    @Mock
-    TestElementAdapter mAdapter;
-    @Mock
-    TestElementAdapter mAdapter2;
-    @Mock
-    TestElementAdapter mAdapter3;
-    @Mock
-    TestElementAdapter mAdapter4;
-
-    private Context mTestContext;
-
-    @Before
-    public void setUp() {
-        mTestContext = Robolectric.buildActivity(Activity.class).get();
-        initMocks(this);
-    }
-
-    @Test
-    public void testPutAndGetOneElement() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        pool.put(KEY1, mAdapter);
-        assertThat(pool.get(KEY1)).isEqualTo(mAdapter);
-    }
-
-    @Test
-    public void testGetFromEmptyPoolReturnsNull() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        assertThat(pool.get(KEY1)).isNull();
-    }
-
-    @Test
-    public void testGetFromEmptyPoolWithDifferentKeyReturnsNull() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        pool.put(KEY1, mAdapter);
-        assertThat(pool.get(KEY2)).isNull();
-    }
-
-    @Test
-    public void testPutAndGetElementsWithDifferentKeys() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        pool.put(KEY1, mAdapter);
-        pool.put(KEY2, mAdapter2);
-        assertThat(pool.get(KEY2)).isEqualTo(mAdapter2);
-        assertThat(pool.get(KEY1)).isEqualTo(mAdapter);
-    }
-
-    @Test
-    public void testPutNullElementFails() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        pool.put(KEY1, null);
-        assertThat(pool.get(KEY1)).isNull();
-    }
-
-    @Test
-    public void testPutNullKeyFails() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        assertThatRunnable(() -> pool.put(null, mAdapter))
-                .throwsAnExceptionOfType(NullPointerException.class)
-                .that()
-                .hasMessageThat()
-                .contains("null key for mAdapter");
-    }
-
-    @Test
-    public void testGetNullKeyReturnsNull() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, CAPACITY);
-        assertThat(pool.get(null)).isNull();
-    }
-
-    @Test
-    public void testCacheOverflowEjectsPool() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(2, CAPACITY);
-        pool.put(KEY1, mAdapter);
-        pool.put(KEY2, mAdapter2);
-        pool.put(KEY3, mAdapter3);
-        assertThat(pool.get(KEY1)).isNull();
-        assertThat(pool.get(KEY2)).isEqualTo(mAdapter2);
-        assertThat(pool.get(KEY3)).isEqualTo(mAdapter3);
-    }
-
-    @Test
-    public void testOverflowSinglePoolIgnoresLastElement() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, 2);
-        pool.put(KEY1, mAdapter);
-        pool.put(KEY1, mAdapter2);
-        pool.put(KEY1, mAdapter3);
-        assertThat(pool.get(KEY1)).isNotNull();
-        assertThat(pool.get(KEY1)).isNotNull();
-        assertThat(pool.get(KEY1)).isNull();
-    }
-
-    @Test
-    public void testFillAllPools() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(2, 2);
-        pool.put(KEY1, mAdapter);
-        pool.put(KEY1, mAdapter2);
-        pool.put(KEY2, mAdapter3);
-        pool.put(KEY2, mAdapter4);
-        assertThat(pool.get(KEY1)).isNotNull();
-        assertThat(pool.get(KEY1)).isNotNull();
-        assertThat(pool.get(KEY1)).isNull();
-        assertThat(pool.get(KEY2)).isNotNull();
-        assertThat(pool.get(KEY2)).isNotNull();
-        assertThat(pool.get(KEY2)).isNull();
-    }
-
-    @Test
-    public void testClear() {
-        KeyedRecyclerPool<TestElementAdapter> pool = new KeyedRecyclerPool<>(MAX_KEYS, MAX_KEYS);
-        pool.put(KEY1, mAdapter);
-        pool.put(KEY1, mAdapter2);
-        pool.put(KEY2, mAdapter3);
-        pool.put(KEY2, mAdapter4);
-        pool.clear();
-        assertThat(pool.get(KEY1)).isNull();
-        assertThat(pool.get(KEY2)).isNull();
-    }
-
-    private static class TestRecyclerKey extends RecyclerKey {
-        private final String mKey;
-
-        TestRecyclerKey(String key) {
-            this.mKey = key;
-        }
-
-        @Override
-        public int hashCode() {
-            return mKey.hashCode();
-        }
-
-        @Override
-        public boolean equals(/*@Nullable*/ Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (obj == null) {
-                return false;
-            }
-
-            if (!(obj instanceof TestRecyclerKey)) {
-                return false;
-            }
-
-            TestRecyclerKey otherKey = (TestRecyclerKey) obj;
-            return otherKey.mKey.equals(this.mKey);
-        }
-    }
-
-    private class TestElementAdapter extends ElementAdapter<View, Object> {
-        TestElementAdapter() {
-            super(mTestContext, null, new View(mTestContext));
-        }
-
-        @Override
-        protected Object getModelFromElement(Element baseElement) {
-            return null;
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/LoadImageCallbackTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/LoadImageCallbackTest.java
deleted file mode 100644
index 6948b8e..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/LoadImageCallbackTest.java
+++ /dev/null
@@ -1,189 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.shadows.ShadowLooper;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link LoadImageCallback}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@org.robolectric.annotation.Config(sdk = 27, manifest = org.robolectric.annotation.Config.NONE)
-public class LoadImageCallbackTest {
-    private static final long FADE_IMAGE_THRESHOLD_MS = 682L;
-    // null is a special value indicating no overlay.
-    private static final Integer NO_OVERLAY_COLOR = null;
-    private static final boolean FADE_IMAGE = true;
-    private static final boolean NO_FADE_IMAGE = false;
-
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-    @Mock
-    private FrameContext mFrameContext;
-
-    private final Drawable mInitialDrawable = new ColorDrawable(Color.RED);
-    private final Drawable mFinalDrawable =
-            new BitmapDrawable(Bitmap.createBitmap(12, 34, Config.ARGB_8888));
-    private final FakeClock mClock = new FakeClock();
-    private AdapterParameters mParameters;
-    private Context mContext;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mAssetProvider.getFadeImageThresholdMs()).thenReturn(FADE_IMAGE_THRESHOLD_MS);
-        when(mFrameContext.getFrameView()).thenReturn(new FrameLayout(mContext));
-
-        mParameters = new AdapterParameters(mContext, null, mHostProviders, null,
-                mock(ElementAdapterFactory.class), mock(TemplateBinder.class), mClock);
-    }
-
-    @Test
-    public void testInitialDrawable_fromImageView() {
-        ImageView imageView = new ImageView(mContext);
-        imageView.setImageDrawable(mInitialDrawable);
-
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, FADE_IMAGE, mParameters, mFrameContext);
-
-        mClock.advance(FADE_IMAGE_THRESHOLD_MS + 1);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable()).isInstanceOf(TransitionDrawable.class);
-
-        TransitionDrawable drawable = (TransitionDrawable) imageView.getDrawable();
-
-        assertThat(drawable.getDrawable(0)).isSameInstanceAs(mInitialDrawable);
-        assertThat(drawable.getDrawable(1)).isSameInstanceAs(mFinalDrawable);
-    }
-
-    @Test
-    public void testInitialDrawable_transparent() {
-        ImageView imageView = new ImageView(mContext);
-
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, FADE_IMAGE, mParameters, mFrameContext);
-
-        mClock.advance(FADE_IMAGE_THRESHOLD_MS + 1);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable()).isInstanceOf(TransitionDrawable.class);
-
-        TransitionDrawable drawable = (TransitionDrawable) imageView.getDrawable();
-
-        assertThat(((ColorDrawable) drawable.getDrawable(0)).getColor())
-                .isEqualTo(Color.TRANSPARENT);
-    }
-
-    @Test
-    public void testQuickLoad_doesntFade_fadingDisabled() {
-        ImageView imageView = new ImageView(mContext);
-
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, NO_FADE_IMAGE, mParameters, mFrameContext);
-
-        mClock.advance(FADE_IMAGE_THRESHOLD_MS + 1);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable()).isSameInstanceAs(mFinalDrawable);
-    }
-
-    @Test
-    public void testQuickLoad_doesntFade_loadsBeforeTimeout() {
-        ImageView imageView = new ImageView(mContext);
-
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, FADE_IMAGE, mParameters, mFrameContext);
-
-        mClock.advance(FADE_IMAGE_THRESHOLD_MS - 1);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable()).isSameInstanceAs(mFinalDrawable);
-    }
-
-    @Test
-    public void testDelayedTask() {
-        ImageView imageView = new ImageView(mContext);
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, FADE_IMAGE, mParameters, mFrameContext);
-        mClock.advance(FADE_IMAGE_THRESHOLD_MS + 1);
-        callback.accept(mFinalDrawable);
-
-        // Starts as transition drawable.
-        assertThat(imageView.getDrawable()).isInstanceOf(TransitionDrawable.class);
-
-        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-
-        // After the delayed task is run, the drawable is set to the mFinalDrawable instead of the
-        // TransitionDrawable to allow for garbage collection of the TransitionDrawable and
-        // initialDrawable.
-        assertThat(imageView.getDrawable()).isSameInstanceAs(mFinalDrawable);
-    }
-
-    @Test
-    public void testSetsScaleType() {
-        ImageView imageView = new ImageView(mContext);
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, NO_FADE_IMAGE, mParameters, mFrameContext);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getScaleType()).isEqualTo(ScaleType.CENTER);
-    }
-
-    @Test
-    public void testSetsOverlayColor() {
-        int color = 0xFEEDFACE;
-        ImageView imageView = new ImageView(mContext);
-        LoadImageCallback callback = new LoadImageCallback(
-                imageView, ScaleType.CENTER, color, NO_FADE_IMAGE, mParameters, mFrameContext);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable().getColorFilter())
-                .isEqualTo(new PorterDuffColorFilter(color, Mode.SRC_IN));
-    }
-
-    @Test
-    public void testSetsOverlayColor_null() {
-        ImageView imageView = new ImageView(mContext);
-        LoadImageCallback callback = new LoadImageCallback(imageView, ScaleType.CENTER,
-                NO_OVERLAY_COLOR, NO_FADE_IMAGE, mParameters, mFrameContext);
-        callback.accept(mFinalDrawable);
-
-        assertThat(imageView.getDrawable().getColorFilter()).isNull();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/MediaQueryHelperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/MediaQueryHelperTest.java
deleted file mode 100644
index 03f4139..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/MediaQueryHelperTest.java
+++ /dev/null
@@ -1,282 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.ComparisonCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition.DarkLightMode;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.FrameWidthCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.MediaQueryCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.OrientationCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.OrientationCondition.Orientation;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.List;
-
-/** Tests for the MediaQueryHelper. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class MediaQueryHelperTest {
-    private static final int DEFAULT_FRAME_WIDTH = 123;
-
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private Context mContext;
-
-    private MediaQueryHelper mMediaQueryHelper;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-    }
-
-    @Test
-    public void testMediaQueryConditions_frameWidth() {
-        int smallWidth = 1;
-        int midWidth = 10;
-        int bigWidth = 100;
-
-        mMediaQueryHelper = new MediaQueryHelper(midWidth, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(FrameWidthCondition.newBuilder()
-                                                          .setWidth(midWidth)
-                                                          .setCondition(ComparisonCondition.EQUALS))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(FrameWidthCondition.newBuilder()
-                                                          .setWidth(smallWidth)
-                                                          .setCondition(ComparisonCondition.EQUALS))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(
-                                           FrameWidthCondition.newBuilder()
-                                                   .setWidth(smallWidth)
-                                                   .setCondition(ComparisonCondition.GREATER_THAN))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(
-                                           FrameWidthCondition.newBuilder()
-                                                   .setWidth(bigWidth)
-                                                   .setCondition(ComparisonCondition.GREATER_THAN))
-                                   .build()))
-                .isFalse();
-        assertThat(
-                mMediaQueryHelper.isMediaQueryMet(
-                        MediaQueryCondition.newBuilder()
-                                .setFrameWidth(FrameWidthCondition.newBuilder()
-                                                       .setWidth(bigWidth)
-                                                       .setCondition(ComparisonCondition.LESS_THAN))
-                                .build()))
-                .isTrue();
-        assertThat(
-                mMediaQueryHelper.isMediaQueryMet(
-                        MediaQueryCondition.newBuilder()
-                                .setFrameWidth(FrameWidthCondition.newBuilder()
-                                                       .setWidth(smallWidth)
-                                                       .setCondition(ComparisonCondition.LESS_THAN))
-                                .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(
-                                           FrameWidthCondition.newBuilder()
-                                                   .setWidth(smallWidth)
-                                                   .setCondition(ComparisonCondition.NOT_EQUALS))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setFrameWidth(
-                                           FrameWidthCondition.newBuilder()
-                                                   .setWidth(midWidth)
-                                                   .setCondition(ComparisonCondition.NOT_EQUALS))
-                                   .build()))
-                .isFalse();
-
-        assertThatRunnable(
-                ()
-                        -> mMediaQueryHelper.isMediaQueryMet(
-                                MediaQueryCondition.newBuilder()
-                                        .setFrameWidth(
-                                                FrameWidthCondition.newBuilder()
-                                                        .setWidth(midWidth)
-                                                        .setCondition(
-                                                                ComparisonCondition.UNSPECIFIED))
-                                        .build()))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Unhandled ComparisonCondition: UNSPECIFIED");
-    }
-
-    @Test
-    public void testMediaQueryConditions_orientation() {
-        mContext.getResources().getConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.LANDSCAPE))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.PORTRAIT))
-                                   .build()))
-                .isFalse();
-
-        mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.LANDSCAPE))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.PORTRAIT))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.UNSPECIFIED))
-                                   .build()))
-                .isTrue();
-
-        mContext.getResources().getConfiguration().orientation =
-                Configuration.ORIENTATION_UNDEFINED;
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.LANDSCAPE))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.PORTRAIT))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                           Orientation.UNSPECIFIED))
-                                   .build()))
-                .isFalse();
-    }
-
-    @Test
-    public void testMediaQueryConditions_darkLight() {
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.DARK))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.LIGHT))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.UNSPECIFIED))
-                                   .build()))
-                .isFalse();
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(false);
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.DARK))
-                                   .build()))
-                .isFalse();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.LIGHT))
-                                   .build()))
-                .isTrue();
-        assertThat(mMediaQueryHelper.isMediaQueryMet(
-                           MediaQueryCondition.newBuilder()
-                                   .setDarkLight(DarkLightCondition.newBuilder().setMode(
-                                           DarkLightMode.UNSPECIFIED))
-                                   .build()))
-                .isTrue();
-    }
-
-    @Test
-    public void testMediaQueryConditions_allConditionsTrue() {
-        MediaQueryCondition landscapeCondition =
-                MediaQueryCondition.newBuilder()
-                        .setOrientation(OrientationCondition.newBuilder().setOrientation(
-                                Orientation.LANDSCAPE))
-                        .build();
-        MediaQueryCondition darkThemeCondition =
-                MediaQueryCondition.newBuilder()
-                        .setDarkLight(DarkLightCondition.newBuilder().setMode(DarkLightMode.DARK))
-                        .build();
-        MediaQueryCondition frameWidthCondition =
-                MediaQueryCondition.newBuilder()
-                        .setFrameWidth(FrameWidthCondition.newBuilder().setWidth(100).setCondition(
-                                ComparisonCondition.GREATER_THAN))
-                        .build();
-
-        List<MediaQueryCondition> conditions =
-                Arrays.asList(landscapeCondition, darkThemeCondition, frameWidthCondition);
-
-        mContext.getResources().getConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.areMediaQueriesMet(conditions)).isTrue();
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(false);
-        mMediaQueryHelper = new MediaQueryHelper(DEFAULT_FRAME_WIDTH, mAssetProvider, mContext);
-        assertThat(mMediaQueryHelper.areMediaQueriesMet(conditions)).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/NoKeyOverwriteHashMapTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/NoKeyOverwriteHashMapTest.java
deleted file mode 100644
index aacde205..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/NoKeyOverwriteHashMapTest.java
+++ /dev/null
@@ -1,36 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-
-/** Tests of the NoKeyOverwriteHashMap */
-@RunWith(JUnit4.class)
-public class NoKeyOverwriteHashMapTest {
-    private final NoKeyOverwriteHashMap<String, String> mMap =
-            new NoKeyOverwriteHashMap<>("Acronym", ErrorCode.ERR_DUPLICATE_BINDING_VALUE);
-
-    @Test
-    public void testPutTwoDifferentKeys() {
-        mMap.put("CPA", "Certified Public Accountant");
-        mMap.put("CPU", "Central Processing Unit");
-    }
-
-    @Test
-    public void testPutTwoSameKeysThrows() {
-        mMap.put("CD", "Compact Disc");
-        assertThatRunnable(() -> mMap.put("CD", "Certificate of Deposit"))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Acronym key 'CD' already defined");
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterFactoryTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterFactoryTest.java
deleted file mode 100644
index d49352eaa..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterFactoryTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.TextElementAdapter.TextElementKey;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link TextElementAdapter} instance of the {@link AdapterFactory}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ParameterizedTextElementAdapterFactoryTest {
-    private static final String TEXT_LINE_CONTENT = "Content";
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    ParameterizedTextElementAdapter.KeySupplier mKeySupplier;
-    @Mock
-    ParameterizedTextElementAdapter mAdapter;
-    @Mock
-    ParameterizedTextElementAdapter mAdapter2;
-
-    private AdapterParameters mAdapterParameters;
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mAdapterParameters = new AdapterParameters(null, null,
-                new HostProviders(mock(AssetProvider.class), null, null, null), new FakeClock(),
-                false, false);
-        when(mKeySupplier.getAdapter(mContext, mAdapterParameters))
-                .thenReturn(mAdapter)
-                .thenReturn(mAdapter2);
-    }
-
-    @Test
-    public void testKeySupplier() {
-        String styleId = "text";
-        StyleIdsStack style = StyleIdsStack.newBuilder().addStyleIds(styleId).build();
-        Font font = Font.newBuilder().setSize(123).setItalic(true).build();
-        Element model = Element.newBuilder()
-                                .setStyleReferences(style)
-                                .setTextElement(TextElement.getDefaultInstance())
-                                .build();
-        StyleProvider styleProvider = mock(StyleProvider.class);
-        when(mFrameContext.makeStyleFor(style)).thenReturn(styleProvider);
-        when(styleProvider.getFont()).thenReturn(font);
-        ParameterizedTextElementAdapter.KeySupplier keySupplier =
-                new ParameterizedTextElementAdapter.KeySupplier();
-
-        assertThat(keySupplier.getAdapterTag()).isEqualTo("ParameterizedTextElementAdapter");
-
-        assertThat(keySupplier.getAdapter(mContext, mAdapterParameters)).isNotNull();
-
-        TextElementKey key = new TextElementKey(font);
-        assertThat(keySupplier.getKey(mFrameContext, model)).isEqualTo(key);
-    }
-
-    @Test
-    public void testGetAdapterFromFactory() {
-        AdapterFactory<ParameterizedTextElementAdapter, Element> textElementFactory =
-                new AdapterFactory<>(mContext, mAdapterParameters, mKeySupplier);
-        Element textElement = getBaseTextElementModel(null);
-
-        ParameterizedTextElementAdapter textElementAdapter =
-                textElementFactory.get(textElement, mFrameContext);
-
-        // Verify we get the adapter from the KeySupplier, and we create but not bind it.
-        assertThat(textElementAdapter).isSameInstanceAs(mAdapter);
-        verify(mAdapter, never()).createAdapter(any(), any(), any());
-        verify(mAdapter, never()).bindModel(any(), any(), any());
-    }
-
-    @Test
-    public void testReleaseAndRecycling() {
-        AdapterFactory<ParameterizedTextElementAdapter, Element> textElementFactory =
-                new AdapterFactory<>(mContext, mAdapterParameters, mKeySupplier);
-        Element textElement = getBaseTextElementModel(null);
-        TextElementKey adapterKey =
-                new TextElementKey(mAdapterParameters.mDefaultStyleProvider.getFont());
-        when(mAdapter.getKey()).thenReturn(adapterKey);
-        when(mKeySupplier.getKey(mFrameContext, textElement)).thenReturn(adapterKey);
-
-        ParameterizedTextElementAdapter textElementAdapter =
-                textElementFactory.get(textElement, mFrameContext);
-        assertThat(textElementAdapter).isSameInstanceAs(mAdapter);
-        textElementAdapter.createAdapter(textElement, mFrameContext);
-
-        // Ensure that releasing in the factory releases the adapter.
-        textElementFactory.release(textElementAdapter);
-        verify(mAdapter).releaseAdapter();
-
-        // Verify we get the same item when we create it again.
-        TextElementAdapter textElementAdapter2 = textElementFactory.get(textElement, mFrameContext);
-        assertThat(textElementAdapter2).isSameInstanceAs(mAdapter);
-        assertThat(textElementAdapter2).isEqualTo(textElementAdapter);
-        verify(mAdapter, never())
-                .createAdapter(textElement, Element.getDefaultInstance(), mFrameContext);
-        verify(mAdapter, never()).bindModel(any(), any(), any());
-
-        // Verify we get a new item when we create another.
-        TextElementAdapter textElementAdapter3 = textElementFactory.get(textElement, mFrameContext);
-        assertThat(textElementAdapter3).isSameInstanceAs(mAdapter2);
-        assertThat(textElementAdapter3).isNotSameInstanceAs(textElementAdapter);
-    }
-
-    private Element getBaseTextElementModel(/*@Nullable*/ StyleProvider styleProvider) {
-        return Element.newBuilder().setTextElement(getBaseTextElement(styleProvider)).build();
-    }
-
-    private TextElement getBaseTextElement(/*@Nullable*/ StyleProvider styleProvider) {
-        StyleProvider sp =
-                styleProvider != null ? styleProvider : mAdapterParameters.mDefaultStyleProvider;
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(sp);
-
-        TextElement.Builder textElement = TextElement.newBuilder();
-        ParameterizedText text = ParameterizedText.newBuilder().setText(TEXT_LINE_CONTENT).build();
-        textElement.setParameterizedText(text);
-        return textElement.build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterTest.java
deleted file mode 100644
index 8170a1f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterTest.java
+++ /dev/null
@@ -1,231 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.Gravity;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests of the {@link ParameterizedTextElementAdapter}; also tests base features of {@link
- * TextElementAdapter}.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ParameterizedTextElementAdapterTest {
-    private static final String TEXT_LINE_CONTENT = "Content";
-    private static final String BINDING = "binding";
-    private static final ParameterizedTextBindingRef DEFAULT_BINDING_REF =
-            ParameterizedTextBindingRef.newBuilder().setBindingId(BINDING).build();
-
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private StyleProvider mMockStyleProvider;
-    @Mock
-    private HostProviders mHostProviders;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private AdapterParameters mAdapterParameters;
-
-    private Context mContext;
-
-    private ParameterizedTextElementAdapter mAdapter;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mHostProviders.getAssetProvider()).thenReturn(mAssetProvider);
-        when(mAssetProvider.isRtL()).thenReturn(false);
-        when(mMockStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-        when(mMockStyleProvider.getTextAlignment()).thenReturn(Gravity.START | Gravity.TOP);
-
-        mAdapterParameters = new AdapterParameters(null, null, mHostProviders,
-                new ParameterizedTextEvaluator(new FakeClock()), null, null, new FakeClock());
-
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class)))
-                .thenReturn(mAdapterParameters.mDefaultStyleProvider);
-
-        mAdapter = new ParameterizedTextElementAdapter.KeySupplier().getAdapter(
-                mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testCreate() {
-        assertThat(mAdapter).isNotNull();
-    }
-
-    @Test
-    public void testBindModel_basic() {
-        Element model = getBaseTextElement();
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-        assertThat(mAdapter.getView()).isNotNull();
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText().toString()).isEqualTo(TEXT_LINE_CONTENT);
-    }
-
-    @Test
-    public void testBindModel_noContent() {
-        mAdapter.createAdapter(getBaseTextElement(), mFrameContext);
-        Element model = asElement(TextElement.getDefaultInstance());
-        mAdapter.bindModel(model, mFrameContext);
-
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText().toString()).isEmpty();
-        verify(mFrameContext)
-                .reportMessage(MessageType.ERROR, ErrorCode.ERR_MISSING_OR_UNHANDLED_CONTENT,
-                        "TextElement missing ParameterizedText content; has CONTENT_NOT_SET");
-    }
-
-    @Test
-    public void testBindModel_withBinding_someText() {
-        ParameterizedText parameterizedText =
-                ParameterizedText.newBuilder().setText(TEXT_LINE_CONTENT).build();
-        BindingValue bindingValue =
-                BindingValue.newBuilder().setParameterizedText(parameterizedText).build();
-        when(mFrameContext.getParameterizedTextBindingValue(DEFAULT_BINDING_REF))
-                .thenReturn(bindingValue);
-
-        Element model = getBindingTextElement(null);
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-        assertThat(mAdapter.getView()).isNotNull();
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText().toString()).isEqualTo(TEXT_LINE_CONTENT);
-    }
-
-    @Test
-    public void testBindModel_withBinding_noContent() {
-        when(mFrameContext.getParameterizedTextBindingValue(DEFAULT_BINDING_REF))
-                .thenReturn(BindingValue.newBuilder().setBindingId(BINDING).build());
-
-        Element model = getBindingTextElement(null);
-        mAdapter.createAdapter(model, mFrameContext);
-
-        assertThatRunnable(() -> mAdapter.bindModel(model, mFrameContext))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Parameterized text binding binding had no content");
-    }
-
-    @Test
-    public void testBindModel_withBinding_optionalAbsent() {
-        Element model = getBindingTextElement(null /* StyleProvider*/);
-        mAdapter.createAdapter(model, mFrameContext);
-        Element modelOptionalBinding =
-                asElement(TextElement.newBuilder()
-                                  .setParameterizedTextBinding(
-                                          DEFAULT_BINDING_REF.toBuilder().setIsOptional(true))
-                                  .build());
-        when(mFrameContext.getParameterizedTextBindingValue(
-                     modelOptionalBinding.getTextElement().getParameterizedTextBinding()))
-                .thenReturn(BindingValue.getDefaultInstance());
-
-        mAdapter.bindModel(modelOptionalBinding, mFrameContext);
-
-        assertThat(mAdapter.getView()).isNotNull();
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText().toString()).isEmpty();
-    }
-
-    @Test
-    public void testBindModel_html() {
-        Element model =
-                asElement(TextElement.newBuilder()
-                                  .setParameterizedText(
-                                          ParameterizedText.newBuilder().setIsHtml(true).setText(
-                                                  "<h1>HEADING!</h1>"))
-                                  .build());
-        mAdapter.createAdapter(model, mFrameContext);
-        mAdapter.bindModel(model, mFrameContext);
-        assertThat(mAdapter.getView()).isNotNull();
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText().toString()).isEqualTo("HEADING!\n\n");
-    }
-
-    @Test
-    public void testStyles_padding() {
-        Element model = asElement(TextElement.getDefaultInstance());
-
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(mMockStyleProvider);
-
-        when(mMockStyleProvider.getFont()).thenReturn(Font.getDefaultInstance());
-
-        mAdapter.createAdapter(model, mFrameContext);
-        assertThat(mAdapter.getView()).isNotNull();
-        verify(mMockStyleProvider).applyElementStyles(mAdapter);
-        TextView textView = mAdapter.getBaseView();
-        assertThat(textView).isNotNull();
-    }
-
-    private Element getBindingTextElement(/*@Nullable*/ StyleProvider styleProvider) {
-        StyleProvider sp =
-                styleProvider != null ? styleProvider : mAdapterParameters.mDefaultStyleProvider;
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(sp);
-        return asElement(
-                TextElement.newBuilder().setParameterizedTextBinding(DEFAULT_BINDING_REF).build());
-    }
-
-    private Element getBaseTextElement() {
-        return getBaseTextElement(null);
-    }
-
-    private Element getBaseTextElement(/*@Nullable*/ StyleProvider styleProvider) {
-        StyleProvider sp =
-                styleProvider != null ? styleProvider : mAdapterParameters.mDefaultStyleProvider;
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(sp);
-
-        return asElement(TextElement.newBuilder()
-                                 .setParameterizedText(
-                                         ParameterizedText.newBuilder().setText(TEXT_LINE_CONTENT))
-                                 .build());
-    }
-
-    private static Element asElement(TextElement textElement) {
-        return Element.newBuilder().setTextElement(textElement).build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextEvaluatorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextEvaluatorTest.java
deleted file mode 100644
index c27b342..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextEvaluatorTest.java
+++ /dev/null
@@ -1,139 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText.Parameter;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link ParameterizedTextEvaluator} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-// TODO: Create a test of evaluteHtml
-public class ParameterizedTextEvaluatorTest {
-    private static final int MILLISECONDS_PER_SECOND = 1000;
-    private static final int SECONDS_PER_MINUTE = 60;
-    private static final int MILLISECONDS_PER_MINUTE = SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
-    @Mock
-    private AssetProvider mAssetProvider;
-
-    private ParameterizedTextEvaluator mEvaluator;
-    private FakeClock mClock = new FakeClock();
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mEvaluator = new ParameterizedTextEvaluator(mClock);
-    }
-
-    @Test
-    public void testEvaluate_noText() {
-        ParameterizedText text = ParameterizedText.getDefaultInstance();
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString()).isEmpty();
-    }
-
-    @Test
-    public void testEvaluate_noParameters() {
-        String content = "content";
-        ParameterizedText text = ParameterizedText.newBuilder().setText(content).build();
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString()).isEqualTo(content);
-    }
-
-    @Test
-    public void testEvaluate_time() {
-        String content = "content %s";
-        String time = "10 minutes";
-        when(mAssetProvider.getRelativeElapsedString(anyLong())).thenReturn(time);
-        long initialTime = 10;
-        mClock.set(initialTime * MILLISECONDS_PER_SECOND + 10 * MILLISECONDS_PER_MINUTE);
-        ParameterizedText text =
-                ParameterizedText.newBuilder()
-                        .setText(content)
-                        .addParameters(Parameter.newBuilder().setTimestampSeconds(initialTime))
-                        .build();
-
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString())
-                .isEqualTo(String.format(content, time));
-        verify(mAssetProvider).getRelativeElapsedString(10 * MILLISECONDS_PER_MINUTE);
-    }
-
-    @Test
-    public void testEvaluate_multipleParameters() {
-        String content = "content %s - %s";
-        String time1 = "10 minutes";
-        String time2 = "20 minutes";
-        when(mAssetProvider.getRelativeElapsedString(anyLong()))
-                .thenReturn(time1)
-                .thenReturn(time2);
-        mClock.set(20 * MILLISECONDS_PER_MINUTE);
-
-        ParameterizedText text =
-                ParameterizedText.newBuilder()
-                        .setText(content)
-                        .addParameters(
-                                Parameter.newBuilder().setTimestampSeconds(10 * SECONDS_PER_MINUTE))
-                        .addParameters(Parameter.newBuilder().setTimestampSeconds(0))
-                        .build();
-
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString())
-                .isEqualTo(String.format(content, time1, time2));
-
-        verify(mAssetProvider).getRelativeElapsedString(10 * MILLISECONDS_PER_MINUTE);
-        verify(mAssetProvider).getRelativeElapsedString(20 * MILLISECONDS_PER_MINUTE);
-    }
-
-    @Test
-    public void testEvaluate_html() {
-        String content = "<h1>content</h1>";
-        ParameterizedText text =
-                ParameterizedText.newBuilder().setIsHtml(true).setText(content).build();
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString()).isEqualTo("content\n\n");
-    }
-
-    @Test
-    public void testEvaluate_htmlAndParameters() {
-        String content = "<h1>content %s</h1>";
-        String time1 = "1 second";
-        when(mAssetProvider.getRelativeElapsedString(anyLong())).thenReturn(time1);
-
-        ParameterizedText text =
-                ParameterizedText.newBuilder()
-                        .setIsHtml(true)
-                        .setText(content)
-                        .addParameters(Parameter.newBuilder().setTimestampSeconds(1))
-                        .build();
-        assertThat(mEvaluator.evaluate(mAssetProvider, text).toString())
-                .isEqualTo("content 1 second\n\n");
-    }
-
-    @Test
-    public void testTimeConversion() {
-        String content = "%s";
-        String time = "10 minutes";
-        when(mAssetProvider.getRelativeElapsedString(1000)).thenReturn(time);
-        mClock.set(2000);
-        ParameterizedText.Builder text = ParameterizedText.newBuilder();
-        text.setText(content);
-        text.addParameters(Parameter.newBuilder().setTimestampSeconds(1).build());
-
-        assertThat(mEvaluator.evaluate(mAssetProvider, text.build()).toString())
-                .isEqualTo(String.format(content, time));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietManagerImplTest.java
deleted file mode 100644
index 6f348349..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietManagerImplTest.java
+++ /dev/null
@@ -1,259 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.Clock;
-import org.chromium.chrome.browser.feed.library.common.time.SystemClockImpl;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.EventLogger;
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.ImageLoader;
-import org.chromium.chrome.browser.feed.library.piet.host.LogDataCallback;
-import org.chromium.chrome.browser.feed.library.piet.host.StringFormatter;
-import org.chromium.chrome.browser.feed.library.piet.host.ThrowingCustomElementProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.TypefaceProvider;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-import java.util.Locale;
-
-/** Tests of the {@link PietManagerImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietManagerImplTest {
-    @Mock
-    private ActionHandler mActionHandler;
-    @Mock
-    private EventLogger mEventLogger;
-    @Mock
-    private CustomElementProvider mCustomElementProvider;
-
-    @Mock
-    ImageLoader mImageLoader;
-    @Mock
-    StringFormatter mStringFormatter;
-    @Mock
-    TypefaceProvider mTypefaceProvider;
-    @Mock
-    private PietStylesHelperFactory mStylesHelpers;
-
-    private Context mContext;
-    private ViewGroup mViewGroup1;
-    private ViewGroup mViewGroup2;
-
-    private PietManagerImpl mPietManager;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mViewGroup1 = new LinearLayout(mContext);
-        mViewGroup2 = new FrameLayout(mContext);
-        mPietManager = (PietManagerImpl) new PietManager.Builder()
-                               .setDebugBehavior(DebugBehavior.VERBOSE)
-                               .setCustomElementProvider(mCustomElementProvider)
-                               .build();
-    }
-
-    @Test
-    public void testCreatePietFrameAdapter() {
-        Supplier<ViewGroup> cardViewSupplier = Suppliers.of(mViewGroup1);
-        FrameAdapterImpl frameAdapter =
-                (FrameAdapterImpl) mPietManager.createPietFrameAdapter(cardViewSupplier,
-                        mActionHandler, mEventLogger, mContext, /* logDataCallback= */ null);
-        assertThat(frameAdapter.getParameters().mParentViewSupplier)
-                .isSameInstanceAs(cardViewSupplier);
-    }
-
-    @Test
-    public void testCreatePietFrameAdapter_loggingCallback() {
-        LogDataCallback logDataCallback = new LogDataCallback() {
-            @Override
-            public void onBind(LogData logData, View view) {}
-
-            @Override
-            public void onUnbind(LogData logData, View view) {}
-        };
-        Supplier<ViewGroup> cardViewSupplier = Suppliers.of(mViewGroup1);
-        FrameAdapterImpl frameAdapter = (FrameAdapterImpl) mPietManager.createPietFrameAdapter(
-                cardViewSupplier, mActionHandler, mEventLogger, mContext, logDataCallback);
-        assertThat(frameAdapter.getParameters().mHostProviders.getLogDataCallback())
-                .isEqualTo(logDataCallback);
-    }
-
-    @Test
-    public void testGetAdapterParameters() {
-        Supplier<ViewGroup> viewGroupProducer1 = Suppliers.of(mViewGroup1);
-        Supplier<ViewGroup> viewGroupProducer2 = Suppliers.of(mViewGroup2);
-        Context context1 = Robolectric.buildActivity(Activity.class).get();
-        Context context2 = Robolectric.buildActivity(Activity.class).get();
-
-        AdapterParameters returnParams;
-
-        // Get params for a context that does not exist
-        returnParams = mPietManager.getAdapterParameters(
-                context1, viewGroupProducer1, /* logDataCallback= */ null);
-        assertThat(returnParams.mParentViewSupplier).isEqualTo(viewGroupProducer1);
-        assertThat(returnParams.mContext).isEqualTo(context1);
-
-        // Get params for the same context again (use cached value)
-        returnParams = mPietManager.getAdapterParameters(
-                context1, Suppliers.of(null), /* logDataCallback= */ null);
-        assertThat(returnParams.mParentViewSupplier).isEqualTo(viewGroupProducer1);
-
-        // Get params for a different context
-        returnParams = mPietManager.getAdapterParameters(
-                context2, viewGroupProducer2, /* logDataCallback= */ null);
-        assertThat(returnParams.mParentViewSupplier).isEqualTo(viewGroupProducer2);
-    }
-
-    @Test
-    public void testBuilder_defaults() {
-        PietManagerImpl manager = (PietManagerImpl) new PietManager.Builder().build();
-        AdapterParameters parameters = manager.getAdapterParameters(
-                mContext, Suppliers.of(mViewGroup1), /* logDataCallback= */ null);
-        assertThat(parameters.mHostProviders.getCustomElementProvider())
-                .isInstanceOf(ThrowingCustomElementProvider.class);
-        assertThat(parameters.mClock).isInstanceOf(SystemClockImpl.class);
-        // There's no good way to test the HostBindingProvider.
-
-        FrameAdapterImpl frameAdapter = (FrameAdapterImpl) manager.createPietFrameAdapter(
-                Suppliers.of(mViewGroup1), mActionHandler, mEventLogger, mContext,
-                /* logDataCallback= */ null);
-        FrameContext frameContext = frameAdapter.createFrameContext(
-                Frame.getDefaultInstance(), 0, Collections.emptyList(), mViewGroup2);
-        assertThat(frameContext.getDebugBehavior()).isEqualTo(DebugBehavior.SILENT);
-
-        AssetProvider assetProvider = manager.mAssetProvider;
-
-        assertThat(assetProvider.isDarkTheme()).isFalse();
-        assertThat(assetProvider.getDefaultCornerRadius()).isEqualTo(0);
-        assertThat(assetProvider.getFadeImageThresholdMs()).isEqualTo(Long.MAX_VALUE);
-        assertThat(assetProvider.isRtL()).isFalse();
-    }
-
-    @Test
-    public void testBuilder_rtl() {
-        Locale defaultLocale = Locale.getDefault();
-
-        Locale.setDefault(Locale.forLanguageTag("ar"));
-        assertThat(LayoutUtils.isDefaultLocaleRtl()).isTrue();
-
-        PietManagerImpl manager = (PietManagerImpl) new PietManager.Builder().build();
-
-        assertThat(manager.mAssetProvider.isRtL()).isTrue();
-
-        // Reset the Locale so it doesn't mess up other tests
-        // (Removing this causes failures in Kokoro/Bazel testing)
-        Locale.setDefault(defaultLocale);
-    }
-
-    @Test
-    public void testBuilder_setters() {
-        boolean isRtL = true;
-        boolean isDarkTheme = true;
-        HostBindingProvider hostBindingProvider = new HostBindingProvider();
-        Clock clock = new FakeClock();
-        PietManagerImpl manager = (PietManagerImpl) new PietManager.Builder()
-                                          .setImageLoader(mImageLoader)
-                                          .setStringFormatter(mStringFormatter)
-                                          .setDefaultCornerRadius(Suppliers.of(123))
-                                          .setIsDarkTheme(Suppliers.of(isDarkTheme))
-                                          .setIsRtL(Suppliers.of(isRtL))
-                                          .setFadeImageThresholdMs(Suppliers.of(456L))
-                                          .setTypefaceProvider(mTypefaceProvider)
-                                          .setDebugBehavior(DebugBehavior.VERBOSE)
-                                          .setCustomElementProvider(mCustomElementProvider)
-                                          .setHostBindingProvider(hostBindingProvider)
-                                          .setClock(clock)
-                                          .build();
-        AdapterParameters parameters = manager.getAdapterParameters(
-                mContext, Suppliers.of(mViewGroup1), /* logDataCallback= */ null);
-        assertThat(parameters.mHostProviders.getCustomElementProvider())
-                .isSameInstanceAs(mCustomElementProvider);
-        assertThat(parameters.mHostProviders.getHostBindingProvider())
-                .isSameInstanceAs(hostBindingProvider);
-        assertThat(parameters.mClock).isSameInstanceAs(clock);
-
-        FrameAdapterImpl frameAdapter = (FrameAdapterImpl) manager.createPietFrameAdapter(
-                Suppliers.of(mViewGroup1), mActionHandler, mEventLogger, mContext,
-                /* logDataCallback= */ null);
-        FrameContext frameContext = frameAdapter.createFrameContext(
-                Frame.getDefaultInstance(), 0, Collections.emptyList(), mViewGroup2);
-        assertThat(frameContext.getDebugBehavior()).isEqualTo(DebugBehavior.VERBOSE);
-
-        AssetProvider assetProvider = manager.mAssetProvider;
-
-        Consumer<Drawable> drawableConsumer = drawable -> {};
-        assetProvider.getImage(Image.getDefaultInstance(), 12, 34, drawableConsumer);
-        verify(mImageLoader).getImage(Image.getDefaultInstance(), 12, 34, drawableConsumer);
-
-        assetProvider.getRelativeElapsedString(789);
-        verify(mStringFormatter).getRelativeElapsedString(789);
-
-        Consumer<Typeface> typefaceConsumer = typeface -> {};
-        assetProvider.getTypeface("blah", false, typefaceConsumer);
-        verify(mTypefaceProvider).getTypeface("blah", false, typefaceConsumer);
-
-        assertThat(assetProvider.isDarkTheme()).isEqualTo(isDarkTheme);
-        assertThat(assetProvider.isRtL()).isEqualTo(isRtL);
-
-        assertThat(assetProvider.getDefaultCornerRadius()).isEqualTo(123);
-        assertThat(assetProvider.getFadeImageThresholdMs()).isEqualTo(456);
-    }
-
-    @Test
-    public void testPurgeRecyclerPools() {
-        // Test with null adapterParameters
-        mPietManager.purgeRecyclerPools();
-
-        ElementAdapterFactory mockFactory = mock(ElementAdapterFactory.class);
-        TemplateBinder mockTemplateBinder = mock(TemplateBinder.class);
-        HostProviders hostProviders = mock(HostProviders.class);
-        RoundedCornerMaskCache maskCache = mock(RoundedCornerMaskCache.class);
-        mPietManager.mAdapterParameters = new AdapterParameters(mContext, Suppliers.of(mViewGroup1),
-                hostProviders, null, mockFactory, mockTemplateBinder, new FakeClock(),
-                mStylesHelpers, maskCache, false, false);
-        mPietManager.purgeRecyclerPools();
-        verify(mockFactory).purgeRecyclerPools();
-        verify(mStylesHelpers).purge();
-        verify(maskCache).purge();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietStylesHelperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietStylesHelperTest.java
deleted file mode 100644
index 740881b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietStylesHelperTest.java
+++ /dev/null
@@ -1,555 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperKey;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.ColorStop;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.Fill;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.LinearGradient;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.DarkLightCondition.DarkLightMode;
-import org.chromium.components.feed.core.proto.ui.piet.MediaQueriesProto.MediaQueryCondition;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheets;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.BoundStyle;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Tests of the {@link PietStylesHelper}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietStylesHelperTest {
-    private static final String STYLESHEET_ID_1 = "ss1";
-    private static final String STYLESHEET_ID_2 = "ss2";
-    private static final String STYLE_ID_1 = "s1";
-    private static final String STYLE_ID_2 = "s2";
-    private static final Style STYLE_1 = Style.newBuilder().setStyleId(STYLE_ID_1).build();
-    private static final Style STYLE_2 = Style.newBuilder().setStyleId(STYLE_ID_2).build();
-    private static final String TEMPLATE_ID_1 = "t1";
-    private static final String TEMPLATE_ID_2 = "t2";
-    private static final Template TEMPLATE_1 =
-            Template.newBuilder().setTemplateId(TEMPLATE_ID_1).build();
-    private static final Template TEMPLATE_2 =
-            Template.newBuilder().setTemplateId(TEMPLATE_ID_2).build();
-
-    private static final MediaQueryCondition DARK_CONDITION =
-            MediaQueryCondition.newBuilder()
-                    .setDarkLight(DarkLightCondition.newBuilder().setMode(DarkLightMode.DARK))
-                    .build();
-    private static final MediaQueryCondition LIGHT_CONDITION =
-            MediaQueryCondition.newBuilder()
-                    .setDarkLight(DarkLightCondition.newBuilder().setMode(DarkLightMode.LIGHT))
-                    .build();
-
-    private static final int FRAME_WIDTH_PX = 480;
-
-    private List<PietSharedState> mSharedStates = new ArrayList<>();
-    @Mock
-    FrameContext mFrameContext;
-    @Mock
-    AssetProvider mAssetProvider;
-    DebugLogger mDebugLogger;
-
-    private Context mContext;
-    private PietStylesHelperFactory mHelperFactory;
-    private PietStylesHelper mHelper;
-    private PietSharedState mSharedState1;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        initMocks(this);
-        Stylesheet.Builder stylesheet1 =
-                Stylesheet.newBuilder().setStylesheetId(STYLESHEET_ID_1).addStyles(STYLE_1);
-
-        Stylesheet.Builder stylesheet2 =
-                Stylesheet.newBuilder().setStylesheetId(STYLESHEET_ID_2).addStyles(STYLE_2);
-
-        mSharedState1 = PietSharedState.newBuilder()
-                                .addStylesheets(stylesheet1.build())
-                                .addTemplates(TEMPLATE_1)
-                                .build();
-
-        PietSharedState sharedState2 = PietSharedState.newBuilder()
-                                               .addStylesheets(stylesheet2.build())
-                                               .addTemplates(TEMPLATE_2)
-                                               .build();
-        mSharedStates.add(mSharedState1);
-        mSharedStates.add(sharedState2);
-
-        mDebugLogger = new DebugLogger();
-
-        mHelperFactory = new PietStylesHelperFactory();
-        mHelper = mHelperFactory.get(mSharedStates, newMediaQueryHelper());
-    }
-
-    @Test
-    public void testConstructorRespectsMediaQueryConditions_stylesheets() {
-        Stylesheet darkStylesheet = Stylesheet.newBuilder()
-                                            .setStylesheetId("DARK")
-                                            .addConditions(DARK_CONDITION)
-                                            .addStyles(STYLE_1)
-                                            .build();
-        Stylesheet lightStylesheet = Stylesheet.newBuilder()
-                                             .setStylesheetId("LIGHT")
-                                             .addConditions(LIGHT_CONDITION)
-                                             .addStyles(STYLE_2)
-                                             .build();
-
-        Style darkStyle = Style.newBuilder().setStyleId("dark").build();
-        Style lightStyle = Style.newBuilder().setStyleId("light").build();
-        Stylesheet switchStylesheetDark = Stylesheet.newBuilder()
-                                                  .setStylesheetId("SWITCH")
-                                                  .addConditions(DARK_CONDITION)
-                                                  .addStyles(darkStyle)
-                                                  .build();
-        Stylesheet switchStylesheetLight = Stylesheet.newBuilder()
-                                                   .setStylesheetId("SWITCH")
-                                                   .addConditions(LIGHT_CONDITION)
-                                                   .addStyles(lightStyle)
-                                                   .build();
-
-        PietSharedState sharedState = PietSharedState.newBuilder()
-                                              .addStylesheets(darkStylesheet)
-                                              .addStylesheets(lightStylesheet)
-                                              .addStylesheets(switchStylesheetDark)
-                                              .addStylesheets(switchStylesheetLight)
-                                              .build();
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mHelper = mHelperFactory.get(Collections.singletonList(sharedState), newMediaQueryHelper());
-
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheetIds("DARK").build(), mDebugLogger))
-                .containsExactly(STYLE_ID_1, STYLE_1);
-        assertThat(
-                mHelper.getStylesheetMap(
-                        Stylesheets.newBuilder().addStylesheetIds("LIGHT").build(), mDebugLogger))
-                .isEmpty();
-        assertThat(
-                mHelper.getStylesheetMap(
-                        Stylesheets.newBuilder().addStylesheetIds("SWITCH").build(), mDebugLogger))
-                .containsExactly("dark", darkStyle);
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(false);
-        mHelper = mHelperFactory.get(Collections.singletonList(sharedState), newMediaQueryHelper());
-
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheetIds("DARK").build(), mDebugLogger))
-                .isEmpty();
-        assertThat(
-                mHelper.getStylesheetMap(
-                        Stylesheets.newBuilder().addStylesheetIds("LIGHT").build(), mDebugLogger))
-                .containsExactly(STYLE_ID_2, STYLE_2);
-        assertThat(
-                mHelper.getStylesheetMap(
-                        Stylesheets.newBuilder().addStylesheetIds("SWITCH").build(), mDebugLogger))
-                .containsExactly("light", lightStyle);
-    }
-
-    @Test
-    public void testConstructorRespectsMediaQueryConditions_templates() {
-        Template darkTemplate =
-                Template.newBuilder().setTemplateId("DARK").addConditions(DARK_CONDITION).build();
-        Template lightTemplate =
-                Template.newBuilder().setTemplateId("LIGHT").addConditions(LIGHT_CONDITION).build();
-        Template switchTemplateDark =
-                Template.newBuilder().setTemplateId("SWITCH").addConditions(DARK_CONDITION).build();
-        Template switchTemplateLight = Template.newBuilder()
-                                               .setTemplateId("SWITCH")
-                                               .addConditions(LIGHT_CONDITION)
-                                               .build();
-
-        PietSharedState sharedState = PietSharedState.newBuilder()
-                                              .addTemplates(darkTemplate)
-                                              .addTemplates(lightTemplate)
-                                              .addTemplates(switchTemplateDark)
-                                              .addTemplates(switchTemplateLight)
-                                              .build();
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mHelper = mHelperFactory.get(Collections.singletonList(sharedState), newMediaQueryHelper());
-
-        assertThat(mHelper.getTemplate("DARK")).isSameInstanceAs(darkTemplate);
-        assertThat(mHelper.getTemplate("LIGHT")).isNull();
-        assertThat(mHelper.getTemplate("SWITCH")).isSameInstanceAs(switchTemplateDark);
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(false);
-        mHelper = mHelperFactory.get(Collections.singletonList(sharedState), newMediaQueryHelper());
-
-        assertThat(mHelper.getTemplate("DARK")).isNull();
-        assertThat(mHelper.getTemplate("LIGHT")).isSameInstanceAs(lightTemplate);
-        assertThat(mHelper.getTemplate("SWITCH")).isSameInstanceAs(switchTemplateLight);
-    }
-
-    @Test
-    public void testGetStylesheet() {
-        Map<String, Style> resultMap1 = mHelper.getStylesheetMap(
-                Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID_1).build(), mDebugLogger);
-        assertThat(resultMap1).containsExactly(STYLE_ID_1, STYLE_1);
-
-        // Retrieve map again to test caching
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID_1).build(),
-                           mDebugLogger))
-                .isSameInstanceAs(resultMap1);
-
-        Map<String, Style> resultMap2 = mHelper.getStylesheetMap(
-                Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID_2).build(), mDebugLogger);
-        assertThat(resultMap2).containsExactly(STYLE_ID_2, STYLE_2);
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheetIds(STYLESHEET_ID_2).build(),
-                           mDebugLogger))
-                .isSameInstanceAs(resultMap2);
-    }
-
-    @Test
-    public void testGetStylesheet_withSameStylesheetId() {
-        List<PietSharedState> sharedStates = new ArrayList<>();
-        Stylesheet.Builder stylesheet =
-                Stylesheet.newBuilder().setStylesheetId(STYLESHEET_ID_1).addStyles(STYLE_2);
-        PietSharedState sharedState2 = PietSharedState.newBuilder()
-                                               .addStylesheets(stylesheet.build())
-                                               .addTemplates(TEMPLATE_2)
-                                               .build();
-        sharedStates.add(mSharedState1);
-        sharedStates.add(sharedState2);
-
-        assertThatRunnable(() -> mHelperFactory.get(sharedStates, newMediaQueryHelper()))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Stylesheet key 'ss1' already defined");
-    }
-
-    @Test
-    public void testGetStylesheet_notExist() {
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheetIds("NOT EXIST").build(),
-                           mDebugLogger))
-                .isEmpty();
-    }
-
-    @Test
-    public void testCreateMapFromStylesheet() {
-        Stylesheet.Builder myStyles = Stylesheet.newBuilder();
-        Style sixties = Style.newBuilder().setStyleId("60s").build();
-        myStyles.addStyles(sixties);
-        Style eighties = Style.newBuilder().setStyleId("80s").build();
-        myStyles.addStyles(eighties);
-
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheets(myStyles.build()).build(),
-                           mDebugLogger))
-                .containsExactly("60s", sixties, "80s", eighties);
-    }
-
-    @Test
-    public void testCreateMapFromStylesheet_throwsWhenDuplicateStyleId() {
-        Stylesheet.Builder myStyles = Stylesheet.newBuilder();
-        Style sixties = Style.newBuilder().setStyleId("60s").build();
-        myStyles.addStyles(sixties);
-        Style sixtiesAgain = Style.newBuilder().setStyleId("60s").build();
-        myStyles.addStyles(sixtiesAgain);
-
-        assertThatRunnable(
-                ()
-                        -> mHelper.getStylesheetMap(
-                                Stylesheets.newBuilder().addStylesheets(myStyles.build()).build(),
-                                mDebugLogger))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Style key '60s' already defined");
-    }
-
-    @Test
-    public void testCreateMapFromStylesheet_filtersByMediaQuery() {
-        Stylesheet.Builder myStyles = Stylesheet.newBuilder();
-        Style lightStyle =
-                Style.newBuilder()
-                        .setStyleId("theme")
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.LIGHT)))
-                        .build();
-        myStyles.addStyles(lightStyle);
-        Style darkStyle =
-                Style.newBuilder()
-                        .setStyleId("theme")
-                        .addConditions(MediaQueryCondition.newBuilder().setDarkLight(
-                                DarkLightCondition.newBuilder().setMode(DarkLightMode.DARK)))
-                        .build();
-        myStyles.addStyles(darkStyle);
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(true);
-        mHelper = mHelperFactory.get(mSharedStates, newMediaQueryHelper());
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheets(myStyles.build()).build(),
-                           mDebugLogger))
-                .containsExactly("theme", darkStyle);
-
-        when(mAssetProvider.isDarkTheme()).thenReturn(false);
-        mHelper = mHelperFactory.get(mSharedStates, newMediaQueryHelper());
-        assertThat(mHelper.getStylesheetMap(
-                           Stylesheets.newBuilder().addStylesheets(myStyles.build()).build(),
-                           mDebugLogger))
-                .containsExactly("theme", lightStyle);
-    }
-
-    @Test
-    public void testGetTemplate() {
-        assertThat(mHelper.getTemplate(TEMPLATE_ID_1)).isEqualTo(TEMPLATE_1);
-        assertThat(mHelper.getTemplate(TEMPLATE_ID_2)).isEqualTo(TEMPLATE_2);
-    }
-
-    @Test
-    public void testGetTemplate_notExist() {
-        assertThat(mHelper.getTemplate("NOT EXIST")).isNull();
-    }
-
-    @Test
-    public void testMergeStyleIdsStack() {
-        // These constants are in the final output and are not overridden
-        int baseWidth = 999;
-        int style1Color = 12345;
-        boolean style1Italic = true;
-        int style2MaxLines = 22222;
-        int style2MinHeight = 33333;
-        int style2FontSize = 13;
-        int style1GradientColor = 1234;
-        int style2GradientDirection = 321;
-        int boundStyleColor = 5050;
-        Fill boundStyleBackground =
-                Fill.newBuilder()
-                        .setLinearGradient(
-                                LinearGradient.newBuilder().setDirectionDeg(boundStyleColor))
-                        .build();
-
-        Style baseStyle = Style.newBuilder()
-                                  .setWidth(baseWidth) // Not overridden
-                                  .setColor(888) // Overridden
-                                  .build();
-        String styleId1 = "STYLE1";
-        Style style1 =
-                Style.newBuilder()
-                        .setColor(style1Color) // Not overridden
-                        .setMaxLines(54321) // Overridden
-                        .setFont(Font.newBuilder().setSize(11).setItalic(style1Italic))
-                        .setBackground(Fill.newBuilder().setLinearGradient(
-                                LinearGradient.newBuilder().addStops(
-                                        ColorStop.newBuilder().setColor(style1GradientColor))))
-                        .build();
-        String styleId2 = "STYLE2";
-        Style style2 =
-                Style.newBuilder()
-                        .setMaxLines(style2MaxLines) // Overrides
-                        .setMinHeight(style2MinHeight) // Not an override
-                        .setFont(Font.newBuilder().setSize(style2FontSize)) // Just override size
-                        .setBackground(Fill.newBuilder().setLinearGradient(
-                                LinearGradient.newBuilder().setDirectionDeg(
-                                        style2GradientDirection)))
-                        .build();
-        Map<String, Style> stylesheetMap = new HashMap<>();
-        stylesheetMap.put(styleId1, style1);
-        stylesheetMap.put(styleId2, style2);
-
-        // Test merging a style IDs stack
-        Style mergedStyle = PietStylesHelper.mergeStyleIdsStack(baseStyle,
-                StyleIdsStack.newBuilder().addStyleIds(styleId1).addStyleIds(styleId2).build(),
-                stylesheetMap, mFrameContext);
-
-        assertThat(mergedStyle)
-                .isEqualTo(Style.newBuilder()
-                                   .setColor(style1Color)
-                                   .setMaxLines(style2MaxLines)
-                                   .setMinHeight(style2MinHeight)
-                                   .setFont(Font.newBuilder()
-                                                    .setSize(style2FontSize)
-                                                    .setItalic(style1Italic))
-                                   .setBackground(Fill.newBuilder().setLinearGradient(
-                                           LinearGradient.newBuilder()
-                                                   .addStops(ColorStop.newBuilder().setColor(
-                                                           style1GradientColor))
-                                                   .setDirectionDeg(style2GradientDirection)))
-                                   .setWidth(baseWidth)
-                                   .build());
-
-        // Test merging a style IDs stack with a bound style
-        String styleBindingId = "BOUND_STYLE";
-        BoundStyle boundStyle = BoundStyle.newBuilder()
-                                        .setColor(boundStyleColor)
-                                        .setBackground(boundStyleBackground)
-                                        .build();
-        StyleBindingRef styleBindingRef =
-                StyleBindingRef.newBuilder().setBindingId(styleBindingId).build();
-        when(mFrameContext.getStyleFromBinding(styleBindingRef)).thenReturn(boundStyle);
-
-        mergedStyle = PietStylesHelper.mergeStyleIdsStack(baseStyle,
-                StyleIdsStack.newBuilder()
-                        .addStyleIds(styleId1)
-                        .addStyleIds(styleId2)
-                        .setStyleBinding(styleBindingRef)
-                        .build(),
-                stylesheetMap, mFrameContext);
-        assertThat(mergedStyle)
-                .isEqualTo(Style.newBuilder()
-                                   .setColor(boundStyleColor)
-                                   .setMaxLines(style2MaxLines)
-                                   .setMinHeight(style2MinHeight)
-                                   .setFont(Font.newBuilder()
-                                                    .setSize(style2FontSize)
-                                                    .setItalic(style1Italic))
-                                   .setBackground(Fill.newBuilder().setLinearGradient(
-                                           LinearGradient.newBuilder()
-                                                   .addStops(ColorStop.newBuilder().setColor(
-                                                           style1GradientColor))
-                                                   .setDirectionDeg(boundStyleColor)))
-                                   .setWidth(baseWidth)
-                                   .build());
-
-        // Binding styles fails when frameContext is null.
-        assertThatRunnable(() -> {
-            PietStylesHelper.mergeStyleIdsStack(Style.getDefaultInstance(),
-                    StyleIdsStack.newBuilder()
-                            .addStyleIds(styleId1)
-                            .addStyleIds(styleId2)
-                            .setStyleBinding(styleBindingRef)
-                            .build(),
-                    stylesheetMap, null);
-        })
-                .throwsAnExceptionOfType(NullPointerException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Binding styles not supported when frameContext is null");
-    }
-
-    @Test
-    public void testHashCodeAndEquals() {
-        PietSharedState sharedState = PietSharedState.newBuilder()
-                                              .addStylesheets(Stylesheet.newBuilder().addStyles(
-                                                      Style.newBuilder().setStyleId("style")))
-                                              .build();
-        PietSharedState equalSharedState =
-                PietSharedState.newBuilder()
-                        .addStylesheets(Stylesheet.newBuilder().addStyles(
-                                Style.newBuilder().setStyleId("style")))
-                        .build();
-        PietSharedState differentSharedState =
-                PietSharedState.newBuilder()
-                        .addStylesheets(Stylesheet.newBuilder().addStyles(
-                                Style.newBuilder().setStyleId("NOT_SAME")))
-                        .build();
-
-        MediaQueryHelper mediaQueryHelper = new MediaQueryHelper(123, 4, false, mContext);
-        MediaQueryHelper equalMediaQueryHelper = new MediaQueryHelper(123, 4, false, mContext);
-        MediaQueryHelper differentMediaQueryHelper = new MediaQueryHelper(456, 7, false, mContext);
-
-        // Objects that should be equal
-        assertThat(ImmutableList.of(sharedState).hashCode())
-                .isEqualTo(ImmutableList.of(sharedState).hashCode());
-        assertThat(ImmutableList.of(sharedState).hashCode())
-                .isEqualTo(ImmutableList.of(equalSharedState).hashCode());
-        assertThat(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper))
-                .isEqualTo(
-                        new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper));
-        assertThat(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper))
-                .isEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(equalSharedState), equalMediaQueryHelper));
-        assertThat(new PietStylesHelperKey(
-                           ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper))
-                .isEqualTo(
-                        new PietStylesHelperKey(ImmutableList.of(sharedState, differentSharedState),
-                                equalMediaQueryHelper));
-        assertThat(new PietStylesHelperKey(
-                           ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper))
-                .isEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(differentSharedState, equalSharedState),
-                        equalMediaQueryHelper));
-        assertThat(new PietStylesHelperKey(
-                           ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper))
-                .isEqualTo(new PietStylesHelperKey(
-                        Arrays.asList(sharedState, differentSharedState), equalMediaQueryHelper));
-
-        assertThat(
-                new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper).hashCode())
-                .isEqualTo(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper)
-                                   .hashCode());
-        assertThat(
-                new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper).hashCode())
-                .isEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(equalSharedState), equalMediaQueryHelper)
-                                   .hashCode());
-        assertThat(new PietStylesHelperKey(
-                           ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper)
-                           .hashCode())
-                .isEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(sharedState, differentSharedState), equalMediaQueryHelper)
-                                   .hashCode());
-
-        // Objects that should be different
-        assertThat(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper))
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(differentSharedState), mediaQueryHelper));
-        assertThat(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper))
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(equalSharedState), differentMediaQueryHelper));
-        assertThat(new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper))
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper));
-
-        assertThat(
-                new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper).hashCode())
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(differentSharedState), mediaQueryHelper)
-                                      .hashCode());
-        assertThat(
-                new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper).hashCode())
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(equalSharedState), differentMediaQueryHelper)
-                                      .hashCode());
-        assertThat(
-                new PietStylesHelperKey(ImmutableList.of(sharedState), mediaQueryHelper).hashCode())
-                .isNotEqualTo(new PietStylesHelperKey(
-                        ImmutableList.of(sharedState, differentSharedState), mediaQueryHelper)
-                                      .hashCode());
-    }
-
-    private MediaQueryHelper newMediaQueryHelper() {
-        return new MediaQueryHelper(FRAME_WIDTH_PX, mAssetProvider, mContext);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/SingleKeyRecyclerPoolTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/SingleKeyRecyclerPoolTest.java
deleted file mode 100644
index a9fd759..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/SingleKeyRecyclerPoolTest.java
+++ /dev/null
@@ -1,160 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link SingleKeyRecyclerPool} */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SingleKeyRecyclerPoolTest {
-    public static final TestRecyclerKey DEFAULT_KEY = new TestRecyclerKey("HappyKey");
-    public static final TestRecyclerKey INVALID_KEY = new TestRecyclerKey("INVALID");
-    public static final int DEFAULT_CAPACITY = 2;
-
-    @Mock
-    ElementAdapter<View, Object> mAdapter;
-    @Mock
-    ElementAdapter<View, Object> mAdapter2;
-    @Mock
-    ElementAdapter<View, Object> mAdapter3;
-
-    SingleKeyRecyclerPool<ElementAdapter<View, Object>> mPool;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mPool = new SingleKeyRecyclerPool<>(DEFAULT_KEY, DEFAULT_CAPACITY);
-    }
-
-    @Test
-    public void testPutAndGetOneElement() {
-        mPool.put(DEFAULT_KEY, mAdapter);
-        assertThat(mPool.get(DEFAULT_KEY)).isEqualTo(mAdapter);
-    }
-
-    @Test
-    public void testGetOnEmptyPoolReturnsNull() {
-        assertThat(mPool.get(DEFAULT_KEY)).isNull();
-    }
-
-    @Test
-    public void testGetWithDifferentKeyFails() {
-        assertThatRunnable(() -> mPool.get(INVALID_KEY))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("does not match singleton key");
-    }
-
-    @Test
-    public void testPutWithDifferentKeyFails() {
-        assertThatRunnable(() -> mPool.put(INVALID_KEY, mAdapter))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("does not match singleton key");
-    }
-
-    @Test
-    public void testGetWithNullKeyFails() {
-        assertThatRunnable(() -> mPool.get(null))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("does not match singleton key");
-    }
-
-    @Test
-    public void testPutWithNullKeyFails() {
-        assertThatRunnable(() -> mPool.put(null, mAdapter))
-                .throwsAnExceptionOfType(NullPointerException.class)
-                .that()
-                .hasMessageThat()
-                .contains("null key for mAdapter");
-    }
-
-    @Test
-    public void testPutSameElementTwiceFails() {
-        mPool.put(DEFAULT_KEY, mAdapter);
-        assertThatRunnable(() -> mPool.put(DEFAULT_KEY, mAdapter))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Already in the pool!");
-    }
-
-    @Test
-    public void testPutAndGetTwoElements() {
-        mPool.put(DEFAULT_KEY, mAdapter);
-        mPool.put(DEFAULT_KEY, mAdapter2);
-        assertThat(mPool.get(DEFAULT_KEY)).isNotNull();
-        assertThat(mPool.get(DEFAULT_KEY)).isNotNull();
-        assertThat(mPool.get(DEFAULT_KEY)).isNull();
-    }
-
-    @Test
-    public void testPoolOverflowIgnoresLastElement() {
-        mPool.put(DEFAULT_KEY, mAdapter);
-        mPool.put(DEFAULT_KEY, mAdapter2);
-        mPool.put(DEFAULT_KEY, mAdapter3);
-        assertThat(mPool.get(DEFAULT_KEY)).isNotNull();
-        assertThat(mPool.get(DEFAULT_KEY)).isNotNull();
-        assertThat(mPool.get(DEFAULT_KEY)).isNull();
-    }
-
-    @Test
-    public void testClear() {
-        mPool.put(DEFAULT_KEY, mAdapter);
-        mPool.put(DEFAULT_KEY, mAdapter2);
-        mPool.clear();
-        assertThat(mPool.get(DEFAULT_KEY)).isNull();
-    }
-
-    private static class TestRecyclerKey extends RecyclerKey {
-        private final String mKey;
-
-        TestRecyclerKey(String key) {
-            this.mKey = key;
-        }
-
-        @Override
-        public int hashCode() {
-            return mKey.hashCode();
-        }
-
-        @Override
-        public boolean equals(/*@Nullable*/ Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (obj == null) {
-                return false;
-            }
-
-            if (!(obj instanceof TestRecyclerKey)) {
-                return false;
-            }
-
-            TestRecyclerKey otherKey = (TestRecyclerKey) obj;
-            return otherKey.mKey.equals(this.mKey);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/StyleProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/StyleProviderTest.java
deleted file mode 100644
index fcd4289..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/StyleProviderTest.java
+++ /dev/null
@@ -1,707 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.piet.StyleProvider.DIMENSION_NOT_SET;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.ui.BorderDrawable;
-import org.chromium.chrome.browser.feed.library.piet.ui.GradientDrawable;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerWrapperView;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.ColorStop;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.Fill;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.LinearGradient;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image.ScaleType;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.ShadowsProto.ElevationShadow;
-import org.chromium.components.feed.core.proto.ui.piet.ShadowsProto.Shadow;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders.Edges;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.EdgeWidths;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.GravityHorizontal;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.GravityVertical;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.ImageLoadingSettings;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.RelativeSize;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.TextAlignmentHorizontal;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.TextAlignmentVertical;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** TODO: Test remaining methods of StyleProvider. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StyleProviderTest {
-    @Mock
-    private AssetProvider mMockAssetProvider;
-    @Mock
-    private ElementAdapter<View, ?> mAdapter;
-    @Mock
-    private TextElementAdapter mTextAdapter;
-    @Mock
-    private RoundedCornerMaskCache mMaskCache;
-
-    private View mView;
-    private View mBaseView;
-    private TextView mTextView;
-
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mView = new View(mContext);
-        mBaseView = new View(mContext);
-        mTextView = new TextView(mContext);
-        when(mAdapter.getView()).thenReturn(mView);
-        when(mAdapter.getBaseView()).thenReturn(mBaseView);
-        when(mAdapter.getContext()).thenReturn(mContext);
-        when(mTextAdapter.getView()).thenReturn(mView);
-        when(mTextAdapter.getBaseView()).thenReturn(mTextView);
-        when(mTextAdapter.getContext()).thenReturn(mContext);
-        when(mMockAssetProvider.isRtLSupplier()).thenReturn(Suppliers.of(false));
-    }
-
-    @Test
-    public void testGetters_withStyleDefined() {
-        Style style =
-                Style.newBuilder()
-                        .setColor(1)
-                        .setBackground(Fill.newBuilder().setColor(2))
-                        .setImageLoadingSettings(
-                                ImageLoadingSettings.newBuilder()
-                                        .setPreLoadFill(Fill.newBuilder().setColor(9))
-                                        .setFadeInImageOnLoad(true)
-                                        .build())
-                        .setRoundedCorners(RoundedCorners.newBuilder().setRadiusDp(3))
-                        .setFont(Font.newBuilder().setSize(4))
-                        .setMaxLines(5)
-                        .setMinHeight(6)
-                        .setHeight(7)
-                        .setWidth(8)
-                        .setScaleType(ScaleType.CENTER_CROP)
-                        .setGravityHorizontal(GravityHorizontal.GRAVITY_END)
-                        .setGravityVertical(GravityVertical.GRAVITY_BOTTOM)
-                        .setTextAlignmentHorizontal(TextAlignmentHorizontal.TEXT_ALIGNMENT_CENTER)
-                        .setTextAlignmentVertical(TextAlignmentVertical.TEXT_ALIGNMENT_BOTTOM)
-                        .build();
-
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-
-        assertThat(styleProvider.getColor()).isEqualTo(style.getColor());
-        assertThat(styleProvider.getBackground()).isEqualTo(style.getBackground());
-        assertThat(styleProvider.hasPreLoadFill()).isTrue();
-        assertThat(styleProvider.getPreLoadFill())
-                .isEqualTo(style.getImageLoadingSettings().getPreLoadFill());
-        assertThat(styleProvider.getFadeInImageOnLoad())
-                .isEqualTo(style.getImageLoadingSettings().getFadeInImageOnLoad());
-        assertThat(styleProvider.getRoundedCorners()).isEqualTo(style.getRoundedCorners());
-        assertThat(styleProvider.hasRoundedCorners()).isTrue();
-        assertThat(styleProvider.getFont()).isEqualTo(style.getFont());
-        assertThat(styleProvider.getMaxLines()).isEqualTo(style.getMaxLines());
-        assertThat(styleProvider.getHeightSpecPx(mContext))
-                .isEqualTo((int) LayoutUtils.dpToPx(style.getHeight(), mContext));
-        assertThat(styleProvider.getWidthSpecPx(mContext))
-                .isEqualTo((int) LayoutUtils.dpToPx(style.getWidth(), mContext));
-        assertThat(styleProvider.hasHeight()).isTrue();
-        assertThat(styleProvider.hasWidth()).isTrue();
-        assertThat(styleProvider.getScaleType()).isEqualTo(ImageView.ScaleType.CENTER_CROP);
-        assertThat(styleProvider.hasGravityHorizontal()).isTrue();
-        assertThat(styleProvider.getGravityHorizontal(Gravity.CLIP_VERTICAL))
-                .isEqualTo(Gravity.END);
-        assertThat(styleProvider.hasGravityVertical()).isTrue();
-        assertThat(styleProvider.getGravityVertical(Gravity.CLIP_HORIZONTAL))
-                .isEqualTo(Gravity.BOTTOM);
-        assertThat(styleProvider.getGravity(Gravity.FILL)).isEqualTo(Gravity.END | Gravity.BOTTOM);
-        assertThat(styleProvider.getTextAlignment())
-                .isEqualTo(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-    }
-
-    @Test
-    public void testGetters_withEmptyStyleDefined() {
-        Style style = Style.getDefaultInstance();
-
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-
-        assertThat(styleProvider.getColor()).isEqualTo(style.getColor());
-        assertThat(styleProvider.getBackground()).isEqualTo(style.getBackground());
-        assertThat(styleProvider.hasPreLoadFill()).isFalse();
-        assertThat(styleProvider.getPreLoadFill()).isEqualTo(Fill.getDefaultInstance());
-        assertThat(styleProvider.getFadeInImageOnLoad()).isFalse();
-        assertThat(styleProvider.hasRoundedCorners()).isFalse();
-        assertThat(styleProvider.getRoundedCorners())
-                .isEqualTo(RoundedCorners.getDefaultInstance());
-        assertThat(styleProvider.getFont()).isEqualTo(Font.getDefaultInstance());
-        assertThat(styleProvider.getMaxLines()).isEqualTo(style.getMaxLines());
-        assertThat(styleProvider.getHeightSpecPx(mContext)).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(styleProvider.getWidthSpecPx(mContext)).isEqualTo(DIMENSION_NOT_SET);
-        assertThat(styleProvider.hasHeight()).isFalse();
-        assertThat(styleProvider.hasWidth()).isFalse();
-        assertThat(styleProvider.getScaleType()).isEqualTo(ImageView.ScaleType.FIT_CENTER);
-        assertThat(styleProvider.hasGravityHorizontal()).isFalse();
-        assertThat(styleProvider.getGravityHorizontal(Gravity.CLIP_VERTICAL))
-                .isEqualTo(Gravity.CLIP_VERTICAL);
-        assertThat(styleProvider.hasGravityVertical()).isFalse();
-        assertThat(styleProvider.getGravityVertical(Gravity.CLIP_HORIZONTAL))
-                .isEqualTo(Gravity.CLIP_HORIZONTAL);
-        assertThat(styleProvider.getGravity(Gravity.FILL)).isEqualTo(Gravity.FILL);
-        assertThat(styleProvider.getTextAlignment()).isEqualTo(Gravity.START | Gravity.TOP);
-    }
-
-    @Test
-    public void testNoOverlapWithAndroidConstants() {
-        assertThat(DIMENSION_NOT_SET).isNotEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(DIMENSION_NOT_SET).isNotEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(DIMENSION_NOT_SET).isLessThan(0);
-    }
-
-    @Test
-    public void testGetHeightSpecPx() {
-        assertThat(new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                           .getHeightSpecPx(mContext))
-                .isEqualTo(DIMENSION_NOT_SET);
-
-        assertThat(new StyleProvider(Style.newBuilder().setHeight(123).build(), mMockAssetProvider)
-                           .getHeightSpecPx(mContext))
-                .isEqualTo(123);
-
-        assertThat(new StyleProvider(
-                           Style.newBuilder().setRelativeHeight(RelativeSize.FILL_PARENT).build(),
-                           mMockAssetProvider)
-                           .getHeightSpecPx(mContext))
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(new StyleProvider(
-                           Style.newBuilder().setRelativeHeight(RelativeSize.FIT_CONTENT).build(),
-                           mMockAssetProvider)
-                           .getHeightSpecPx(mContext))
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(
-                new StyleProvider(Style.newBuilder()
-                                          .setRelativeHeight(RelativeSize.RELATIVE_SIZE_UNDEFINED)
-                                          .build(),
-                        mMockAssetProvider)
-                        .getHeightSpecPx(mContext))
-                .isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testGetWidthSpecPx() {
-        assertThat(new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                           .getWidthSpecPx(mContext))
-                .isEqualTo(DIMENSION_NOT_SET);
-
-        assertThat(new StyleProvider(Style.newBuilder().setWidth(123).build(), mMockAssetProvider)
-                           .getWidthSpecPx(mContext))
-                .isEqualTo(123);
-
-        assertThat(new StyleProvider(
-                           Style.newBuilder().setRelativeWidth(RelativeSize.FILL_PARENT).build(),
-                           mMockAssetProvider)
-                           .getWidthSpecPx(mContext))
-                .isEqualTo(LayoutParams.MATCH_PARENT);
-        assertThat(new StyleProvider(
-                           Style.newBuilder().setRelativeWidth(RelativeSize.FIT_CONTENT).build(),
-                           mMockAssetProvider)
-                           .getWidthSpecPx(mContext))
-                .isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(new StyleProvider(Style.newBuilder()
-                                             .setRelativeWidth(RelativeSize.RELATIVE_SIZE_UNDEFINED)
-                                             .build(),
-                           mMockAssetProvider)
-                           .getWidthSpecPx(mContext))
-                .isEqualTo(DIMENSION_NOT_SET);
-    }
-
-    @Test
-    public void testHasBorders_falseIfWidthZero() {
-        StyleProvider styleProvider = new StyleProvider(
-                Style.newBuilder().setBorders(Borders.newBuilder().setBitmask(15)).build(),
-                mMockAssetProvider);
-
-        assertThat(styleProvider.hasBorders()).isFalse();
-    }
-
-    @Test
-    public void testHasRoundedCornersMethod_noRoundedCorners() {
-        Style noRoundedCornerStyle = Style.getDefaultInstance();
-        StyleProvider styleProvider = new StyleProvider(noRoundedCornerStyle, mMockAssetProvider);
-        when(mMockAssetProvider.getDefaultCornerRadius()).thenReturn(321);
-
-        assertThat(styleProvider.hasRoundedCorners()).isFalse();
-    }
-
-    @Test
-    public void testApplyPadding() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                .applyPadding(mContext, mView, padding,
-                        TextElementAdapter.ExtraLineHeight.builder().build());
-        verifyPadding(mView, padding);
-    }
-
-    @Test
-    public void testApplyPadding_RtL() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        when(mMockAssetProvider.isRtL()).thenReturn(true);
-        when(mMockAssetProvider.isRtLSupplier()).thenReturn(Suppliers.of(true));
-        new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                .applyPadding(mContext, mView, padding,
-                        TextElementAdapter.ExtraLineHeight.builder().build());
-        assertThat(mView.getPaddingLeft()).isEqualTo(4);
-        assertThat(mView.getPaddingRight()).isEqualTo(3);
-    }
-
-    @Test
-    public void testApplyPadding_lineHeight() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        TextElementAdapter.ExtraLineHeight extraLineHeightPadding =
-                TextElementAdapter.ExtraLineHeight.builder()
-                        .setTopPaddingPx(50)
-                        .setBottomPaddingPx(60)
-                        .build();
-        Style paddingStyle = Style.newBuilder().setPadding(padding).build();
-        StyleProvider styleProvider = new StyleProvider(paddingStyle, mMockAssetProvider);
-
-        styleProvider.applyPadding(mContext, mView, padding, extraLineHeightPadding);
-
-        // line height top padding (50) + regular top padding (1) = expected final top (51)
-        assertThat(mView.getPaddingTop()).isEqualTo(51);
-        // line height bottom padding (60) + regular bottom padding (2) = expected final bottom (62)
-        assertThat(mView.getPaddingBottom()).isEqualTo(62);
-    }
-
-    @Test
-    public void testApplyPadding_addsExtraForBorders_allSides() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        Style bordersStyle =
-                Style.newBuilder().setBorders(Borders.newBuilder().setWidth(5)).build();
-        new StyleProvider(bordersStyle, mMockAssetProvider)
-                .applyPadding(mContext, mView, padding,
-                        TextElementAdapter.ExtraLineHeight.builder().build());
-
-        assertThat(mView.getPaddingTop()).isEqualTo((int) LayoutUtils.dpToPx(1 + 5, mContext));
-        assertThat(mView.getPaddingBottom()).isEqualTo((int) LayoutUtils.dpToPx(2 + 5, mContext));
-        assertThat(mView.getPaddingStart()).isEqualTo((int) LayoutUtils.dpToPx(3 + 5, mContext));
-        assertThat(mView.getPaddingEnd()).isEqualTo((int) LayoutUtils.dpToPx(4 + 5, mContext));
-    }
-
-    @Test
-    public void testApplyPadding_addsExtraForBorders_someSides() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        Style bordersBottomRight =
-                Style.newBuilder()
-                        .setBorders(Borders.newBuilder().setWidth(5).setBitmask(
-                                Edges.BOTTOM.getNumber() | Edges.END.getNumber()))
-                        .build();
-        new StyleProvider(bordersBottomRight, mMockAssetProvider)
-                .applyPadding(mContext, mView, padding,
-                        TextElementAdapter.ExtraLineHeight.builder().build());
-
-        assertThat(mView.getPaddingTop()).isEqualTo((int) LayoutUtils.dpToPx(1, mContext));
-        assertThat(mView.getPaddingBottom()).isEqualTo((int) LayoutUtils.dpToPx(2 + 5, mContext));
-        assertThat(mView.getPaddingStart()).isEqualTo((int) LayoutUtils.dpToPx(3, mContext));
-        assertThat(mView.getPaddingEnd()).isEqualTo((int) LayoutUtils.dpToPx(4 + 5, mContext));
-    }
-
-    @Test
-    public void testCreateBorderDrawable() {
-        Borders borders = Borders.newBuilder()
-                                  .setColor(Color.CYAN)
-                                  .setWidth(4)
-                                  .setBitmask(Edges.TOP.getNumber() | Edges.END.getNumber())
-                                  .build();
-
-        FrameLayout view = new FrameLayout(mContext);
-        new StyleProvider(Style.newBuilder().setBorders(borders).build(), mMockAssetProvider)
-                .addBordersWithoutRoundedCorners(view, mContext);
-        BorderDrawable borderDrawable = (BorderDrawable) view.getForeground();
-        borderDrawable.setBounds(0, 0, 0, 0);
-
-        assertThat(borderDrawable.getPaint().getColor()).isEqualTo(Color.CYAN);
-        assertThat(borderDrawable.getBounds()).isEqualTo(new Rect(-4, 0, 0, 4));
-    }
-
-    @Test
-    public void testCreateBorderDrawable_noOp() {
-        FrameLayout view = new FrameLayout(mContext);
-        new StyleProvider(Style.newBuilder().setBorders(Borders.newBuilder().setWidth(0)).build(),
-                mMockAssetProvider)
-                .addBordersWithoutRoundedCorners(view, mContext);
-
-        assertThat(view.getForeground()).isNull();
-    }
-
-    @Test
-    public void testCreateWrapperView_noRoundedCorners() {
-        StyleProvider styleProvider = new StyleProvider(
-                Style.newBuilder()
-                        .setRoundedCorners(RoundedCorners.newBuilder().setRadiusDp(0).setBitmask(4))
-                        .build(),
-                mMockAssetProvider);
-
-        FrameLayout wrapperView =
-                styleProvider.createWrapperView(mContext, mMaskCache, false, false);
-
-        assertThat(wrapperView).isNotInstanceOf(RoundedCornerWrapperView.class);
-        assertThat(wrapperView.getOutlineProvider()).isEqualTo(ViewOutlineProvider.BOUNDS);
-    }
-
-    @Test
-    public void testCreateWrapperView_roundedCorners() {
-        StyleProvider styleProvider = new StyleProvider(
-                Style.newBuilder()
-                        .setRoundedCorners(
-                                RoundedCorners.newBuilder().setRadiusDp(16).setBitmask(4))
-                        .build(),
-                mMockAssetProvider);
-
-        FrameLayout wrapperView =
-                styleProvider.createWrapperView(mContext, mMaskCache, false, false);
-
-        assertThat(wrapperView).isInstanceOf(RoundedCornerWrapperView.class);
-        assertThat(wrapperView.getOutlineProvider()).isNotEqualTo(ViewOutlineProvider.BOUNDS);
-    }
-
-    @Test
-    public void testApplyMargins() {
-        EdgeWidths margins =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        Style marginStyle = Style.newBuilder().setMargins(margins).build();
-
-        MarginLayoutParams params = new MarginLayoutParams(
-                MarginLayoutParams.MATCH_PARENT, MarginLayoutParams.WRAP_CONTENT);
-
-        new StyleProvider(marginStyle, mMockAssetProvider).applyMargins(mContext, params);
-        verifyMargins(params, margins);
-    }
-
-    @Test
-    public void testElementStyles_padding() {
-        EdgeWidths padding =
-                EdgeWidths.newBuilder().setTop(1).setBottom(2).setStart(3).setEnd(4).build();
-        Style paddingStyle = Style.newBuilder().setPadding(padding).build();
-
-        new StyleProvider(paddingStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-        verifyPadding(mBaseView, padding);
-    }
-
-    @Test
-    public void testElementStyles_backgroundAndCorners() {
-        int color = 0xffffffff;
-        Fill background = Fill.newBuilder().setColor(color).build();
-        RoundedCorners corners = RoundedCorners.newBuilder().setRadiusDp(4).setBitmask(3).build();
-
-        Style style = Style.newBuilder()
-                              .setColor(color)
-                              .setBackground(background)
-                              .setRoundedCorners(corners)
-                              .build();
-
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        styleProvider.applyElementStyles(mAdapter);
-
-        ColorDrawable colorDrawable = (ColorDrawable) mBaseView.getBackground();
-        assertThat(colorDrawable.getColor()).isEqualTo(color);
-    }
-
-    @Test
-    public void testElementStyles_noBackgroundInStyle() {
-        mBaseView.setBackground(new ColorDrawable(0xffff0000));
-
-        new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                .applyElementStyles(mAdapter);
-        assertThat(mBaseView.getBackground()).isNull();
-    }
-
-    @Test
-    public void testElementStyles_noColorInFill() {
-        mBaseView.setBackground(new ColorDrawable(0xffff0000));
-
-        Style style = Style.newBuilder().setBackground(Fill.getDefaultInstance()).build();
-
-        new StyleProvider(style, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getBackground()).isNull();
-    }
-
-    @Test
-    public void testElementStyles_gradientBackground() {
-        mBaseView.setBackground(new ColorDrawable(0xffff0000));
-
-        Style style = Style.newBuilder()
-                              .setBackground(Fill.newBuilder().setLinearGradient(
-                                      LinearGradient.newBuilder()
-                                              .setDirectionDeg(25)
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.RED)
-                                                                .setPositionPercent(0))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.WHITE)
-                                                                .setPositionPercent(25))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.GREEN)
-                                                                .setPositionPercent(50))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.WHITE)
-                                                                .setPositionPercent(75))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.BLUE)
-                                                                .setPositionPercent(100))))
-                              .build();
-
-        new StyleProvider(style, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        Drawable background = mBaseView.getBackground();
-        assertThat(background).isInstanceOf(GradientDrawable.class);
-    }
-
-    @Test
-    public void testElementStyles_gradientBackground_badAngle_doesntCrash() {
-        mBaseView.setBackground(new ColorDrawable(0xffff0000));
-
-        Style style = Style.newBuilder()
-                              .setBackground(Fill.newBuilder().setLinearGradient(
-                                      LinearGradient.newBuilder()
-                                              .setDirectionDeg(999)
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.RED)
-                                                                .setPositionPercent(0))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.BLUE)
-                                                                .setPositionPercent(100))))
-                              .build();
-
-        new StyleProvider(style, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        Drawable background = mBaseView.getBackground();
-        assertThat(background).isInstanceOf(GradientDrawable.class);
-    }
-
-    @Test
-    public void testElementStyles_gradientBackground_badPercent_doesntCrash() {
-        mBaseView.setBackground(new ColorDrawable(0xffff0000));
-
-        Style style = Style.newBuilder()
-                              .setBackground(Fill.newBuilder().setLinearGradient(
-                                      LinearGradient.newBuilder()
-                                              .setDirectionDeg(45)
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.RED)
-                                                                .setPositionPercent(200))
-                                              .addStops(ColorStop.newBuilder()
-                                                                .setColor(Color.BLUE)
-                                                                .setPositionPercent(25))))
-                              .build();
-
-        new StyleProvider(style, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        Drawable background = mBaseView.getBackground();
-        assertThat(background).isInstanceOf(GradientDrawable.class);
-    }
-
-    @Test
-    public void testElementStyles_minimumHeight() {
-        int minHeight = 12345;
-        Style heightStyle = Style.newBuilder().setMinHeight(minHeight).build();
-
-        new StyleProvider(heightStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getMinimumHeight()).isEqualTo(minHeight);
-    }
-
-    @Test
-    public void testElementStyles_noMinimumHeight() {
-        Style noHeightStyle = Style.getDefaultInstance();
-        mView.setMinimumHeight(12345);
-
-        new StyleProvider(noHeightStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getMinimumHeight()).isEqualTo(0);
-    }
-
-    @Test
-    public void testElementStyles_shadow() {
-        int elevation = 5280;
-        Style shadowStyle = Style.newBuilder()
-                                    .setShadow(Shadow.newBuilder().setElevationShadow(
-                                            ElevationShadow.newBuilder().setElevation(elevation)))
-                                    .build();
-
-        new StyleProvider(shadowStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        assertThat(mView.getElevation()).isEqualTo((float) elevation);
-    }
-
-    @Test
-    public void testElementStyles_noShadow() {
-        Style shadowStyle = Style.newBuilder()
-                                    .setShadow(Shadow.newBuilder().setElevationShadow(
-                                            ElevationShadow.newBuilder().setElevation(5280)))
-                                    .build();
-        Style noShadowStyle = Style.getDefaultInstance();
-
-        new StyleProvider(shadowStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        new StyleProvider(noShadowStyle, mMockAssetProvider).applyElementStyles(mAdapter);
-
-        assertThat(mView.getElevation()).isEqualTo(0.0f);
-    }
-
-    @Test
-    public void testElementStyles_opacity() {
-        new StyleProvider(Style.newBuilder().setOpacity(0.5f).build(), mMockAssetProvider)
-                .applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getAlpha()).isEqualTo(0.5f);
-
-        new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider)
-                .applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getAlpha()).isEqualTo(1.0f);
-    }
-
-    @Test
-    public void testElementStyles_overlayResets() {
-        mBaseView.setBackground(new ShapeDrawable());
-        mBaseView.setElevation(12.3f);
-
-        new StyleProvider(Style.newBuilder()
-                                  .setBackground(Fill.newBuilder().setColor(Color.BLUE))
-                                  .setShadow(Shadow.newBuilder().setElevationShadow(
-                                          ElevationShadow.newBuilder().setElevation(5)))
-                                  .build(),
-                mMockAssetProvider)
-                .applyElementStyles(mAdapter);
-
-        assertThat(mBaseView.getElevation()).isWithin(0.1f).of(0.0f);
-        assertThat(mView.getElevation()).isWithin(0.1f).of(5.0f);
-
-        assertThat(mView.getBackground()).isNull();
-        assertThat(mBaseView.getBackground()).isInstanceOf(ColorDrawable.class);
-    }
-
-    @Test
-    public void testCreateBackground() {
-        int color = 12345;
-        Fill fill = Fill.newBuilder().setColor(color).build();
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(4321).build();
-        StyleProvider styleProvider;
-
-        styleProvider = new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider);
-        assertThat(styleProvider.createBackground()).isNull();
-
-        styleProvider = new StyleProvider(
-                Style.newBuilder().setBackground(fill).setRoundedCorners(roundedCorners).build(),
-                mMockAssetProvider);
-        Drawable backgroundDrawable = styleProvider.createBackground();
-        assertThat(backgroundDrawable).isInstanceOf(ColorDrawable.class);
-
-        ColorDrawable background = (ColorDrawable) backgroundDrawable;
-        assertThat(background.getColor()).isEqualTo(color);
-    }
-
-    @Test
-    public void testCreatePreLoadFill() {
-        int color = 12345;
-        Fill fill = Fill.newBuilder().setColor(color).build();
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(4321).build();
-        StyleProvider styleProvider;
-
-        styleProvider = new StyleProvider(Style.getDefaultInstance(), mMockAssetProvider);
-        assertThat(styleProvider.createPreLoadFill()).isNull();
-
-        styleProvider = new StyleProvider(
-                Style.newBuilder()
-                        .setImageLoadingSettings(
-                                ImageLoadingSettings.newBuilder().setPreLoadFill(fill).build())
-                        .setRoundedCorners(roundedCorners)
-                        .build(),
-                mMockAssetProvider);
-        Drawable preLoadFillDrawable = styleProvider.createPreLoadFill();
-        assertThat(preLoadFillDrawable).isInstanceOf(ColorDrawable.class);
-
-        ColorDrawable background = (ColorDrawable) preLoadFillDrawable;
-        assertThat(background.getColor()).isEqualTo(color);
-    }
-
-    @Test
-    public void testEqualsAndHashCode() {
-        Style style = Style.newBuilder().setColor(123).build();
-        Style sameStyle = Style.newBuilder().setColor(123).build();
-        Style differentStyle = Style.newBuilder().setHeight(456).build();
-
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        StyleProvider sameStyleProvider = new StyleProvider(sameStyle, mMockAssetProvider);
-        StyleProvider differentStyleProvider =
-                new StyleProvider(differentStyle, mMockAssetProvider);
-
-        assertThat(styleProvider).isEqualTo(sameStyleProvider);
-        assertThat(styleProvider.hashCode()).isEqualTo(sameStyleProvider.hashCode());
-        assertThat(styleProvider).isNotEqualTo(differentStyleProvider);
-    }
-
-    private void verifyPadding(View view, EdgeWidths padding) {
-        assertThat(view.getPaddingTop())
-                .isEqualTo((int) LayoutUtils.dpToPx(padding.getTop(), mContext));
-        assertThat(view.getPaddingBottom())
-                .isEqualTo((int) LayoutUtils.dpToPx(padding.getBottom(), mContext));
-        assertThat(view.getPaddingStart())
-                .isEqualTo((int) LayoutUtils.dpToPx(padding.getStart(), mContext));
-        assertThat(view.getPaddingEnd())
-                .isEqualTo((int) LayoutUtils.dpToPx(padding.getEnd(), mContext));
-    }
-
-    private void verifyMargins(MarginLayoutParams params, EdgeWidths margins) {
-        assertThat(params.getMarginStart())
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getStart(), mContext));
-        assertThat(params.getMarginEnd())
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getEnd(), mContext));
-        assertThat(params.topMargin)
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getTop(), mContext));
-        assertThat(params.bottomMargin)
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getBottom(), mContext));
-        assertThat(params.leftMargin)
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getStart(), mContext));
-        assertThat(params.rightMargin)
-                .isEqualTo((int) LayoutUtils.dpToPx(margins.getEnd(), mContext));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TemplateBinderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TemplateBinderTest.java
deleted file mode 100644
index e1dfbe8..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TemplateBinderTest.java
+++ /dev/null
@@ -1,724 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.DebugBehavior;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.piet.PietStylesHelper.PietStylesHelperFactory;
-import org.chromium.chrome.browser.feed.library.piet.TemplateBinder.TemplateAdapterModel;
-import org.chromium.chrome.browser.feed.library.piet.TemplateBinder.TemplateKey;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.ParameterizedTextBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingContext;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Content;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.ElementList;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.GridRow;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.Fill;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.PietSharedState;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheet;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Stylesheets;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Template;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for TemplateBinder methods. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TemplateBinderTest {
-    private static final int FRAME_COLOR = 12345;
-    private static final String FRAME_STYLESHEET_ID = "coolcat";
-    private static final String TEXT_STYLE_ID = "catalog";
-    private static final String TEMPLATE_ID = "duplicat";
-    private static final String OTHER_TEMPLATE_ID = "alleycat";
-    private static final String TEXT_BINDING_ID = "ofmiceandmen";
-    private static final String TEXT_CONTENTS = "afewmilessouth";
-
-    private static final int FRAME_WIDTH_PX = 321;
-
-    private static final Template MODEL_TEMPLATE =
-            Template.newBuilder()
-                    .setTemplateId(TEMPLATE_ID)
-                    .setElement(
-                            Element.newBuilder()
-                                    .setStyleReferences(
-                                            StyleIdsStack.newBuilder().addStyleIds(TEXT_STYLE_ID))
-                                    .setTextElement(
-                                            TextElement.newBuilder().setParameterizedTextBinding(
-                                                    ParameterizedTextBindingRef.newBuilder()
-                                                            .setBindingId(TEXT_BINDING_ID))))
-                    .build();
-    private static final Template OTHER_TEMPLATE =
-            Template.newBuilder()
-                    .setTemplateId(OTHER_TEMPLATE_ID)
-                    .setElement(
-                            Element.newBuilder().setElementList(ElementList.getDefaultInstance()))
-                    .build();
-
-    private static final Frame DEFAULT_FRAME =
-            Frame.newBuilder()
-                    .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                            Stylesheet.newBuilder()
-                                    .setStylesheetId(FRAME_STYLESHEET_ID)
-                                    .addStyles(Style.newBuilder()
-                                                       .setStyleId(TEXT_STYLE_ID)
-                                                       .setColor(FRAME_COLOR))))
-                    .addTemplates(MODEL_TEMPLATE)
-                    .addTemplates(OTHER_TEMPLATE)
-                    .build();
-
-    private static final BindingContext MODEL_BINDING_CONTEXT =
-            BindingContext.newBuilder()
-                    .addBindingValues(
-                            BindingValue.newBuilder()
-                                    .setBindingId(TEXT_BINDING_ID)
-                                    .setParameterizedText(
-                                            ParameterizedText.newBuilder().setText(TEXT_CONTENTS)))
-                    .build();
-
-    private static final Element DEFAULT_TEMPLATE_ELEMENT =
-            Element.newBuilder().setElementList(ElementList.getDefaultInstance()).build();
-
-    @Mock
-    private ElementListAdapter mElementListAdapter;
-    @Mock
-    private ElementAdapter<? extends View, ?> mTemplateAdapter;
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private ElementAdapterFactory mAdapterFactory;
-    @Mock
-    private ActionHandler mActionHandler;
-
-    private KeyedRecyclerPool<ElementAdapter<? extends View, ?>> mTemplateRecyclerPool;
-
-    private TemplateBinder mTemplateBinder;
-
-    @Before
-    public void setUp() throws Exception {
-        initMocks(this);
-
-        mTemplateRecyclerPool = new KeyedRecyclerPool<>(100, 100);
-        mTemplateBinder = new TemplateBinder(mTemplateRecyclerPool, mAdapterFactory);
-    }
-
-    @Test
-    public void testCreateTemplateAdapter() {
-        // Set up data for test: template, shared states, binding context, template adapter model
-        String templateId = "papa";
-        Template template = Template.newBuilder()
-                                    .setTemplateId(templateId)
-                                    .setElement(DEFAULT_TEMPLATE_ELEMENT)
-                                    .build();
-        when(mFrameContext.getTemplate(templateId)).thenReturn(template);
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        List<PietSharedState> sharedStates = Collections.singletonList(sharedState);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("potato"))
-                        .build();
-        TemplateAdapterModel model = new TemplateAdapterModel(template, bindingContext);
-
-        // Set frameContext to return a new frameContext when a template is bound
-        FrameContext templateContext = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(template, bindingContext))
-                .thenReturn(templateContext);
-        doReturn(mElementListAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(DEFAULT_TEMPLATE_ELEMENT, templateContext);
-
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createTemplateAdapter(model, mFrameContext);
-
-        assertThat(adapter).isSameInstanceAs(mElementListAdapter);
-        verify(mElementListAdapter)
-                .setKey(new TemplateKey(template, sharedStates, new ArrayList<>()));
-    }
-
-    @Test
-    public void testCreateTemplateAdapter_recycled() {
-        Template template = Template.newBuilder()
-                                    .setTemplateId("template")
-                                    .setElement(DEFAULT_TEMPLATE_ELEMENT)
-                                    .build();
-        List<PietSharedState> sharedStates = Collections.emptyList();
-        TemplateKey templateKey = new TemplateKey(template, sharedStates, new ArrayList<>());
-        when(mTemplateAdapter.getKey()).thenReturn(templateKey);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-
-        // Release adapter to populate the recycler pool
-        mTemplateRecyclerPool.put(templateKey, mTemplateAdapter);
-
-        // Get a new adapter from the pool.
-        TemplateAdapterModel model =
-                new TemplateAdapterModel(template, BindingContext.getDefaultInstance());
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createTemplateAdapter(model, mFrameContext);
-
-        assertThat(adapter).isSameInstanceAs(mTemplateAdapter);
-        // We don't need to re-create the adapter; it has already been created.
-        verify(mTemplateAdapter, never()).createAdapter(any(Element.class), any());
-        verify(mTemplateAdapter, never()).createAdapter(any(), any(), any());
-    }
-
-    /**
-     * Set up a "real" environment, and ensure that styles are set from the template, not from the
-     * frame.
-     */
-    @Test
-    public void testCreateTemplateAdapter_checkStyleSources() {
-        // Set up 3 styles: one on the template, one on the frame, and one on the shared state.
-        String templateStyleId = "templateStyle";
-        String frameStyleId = "frameStyle";
-        String globalStyleId = "globalStyle";
-
-        int templateColor = Color.GREEN;
-        Style templateStyle = Style.newBuilder()
-                                      .setStyleId(templateStyleId)
-                                      .setBackground(Fill.newBuilder().setColor(templateColor))
-                                      .build();
-        int frameColor = Color.RED;
-        Style frameStyle = Style.newBuilder()
-                                   .setStyleId(frameStyleId)
-                                   .setBackground(Fill.newBuilder().setColor(frameColor))
-                                   .build();
-        int globalColor = Color.BLUE;
-        Style globalStyle = Style.newBuilder()
-                                    .setStyleId(globalStyleId)
-                                    .setBackground(Fill.newBuilder().setColor(globalColor))
-                                    .build();
-
-        // Style on the frame with the same ID as the template's style.
-        int frameTemplateColor = Color.MAGENTA;
-        Style frameTemplateStyle =
-                Style.newBuilder()
-                        .setStyleId(templateStyleId)
-                        .setBackground(Fill.newBuilder().setColor(frameTemplateColor))
-                        .build();
-
-        // Template: A list of 3 elements with template style, frame style, and global style,
-        // respectively.
-        Template template =
-                Template.newBuilder()
-                        .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                Stylesheet.newBuilder().addStyles(templateStyle)))
-                        .setElement(Element.newBuilder().setElementList(
-                                ElementList.newBuilder()
-                                        .addContents(Content.newBuilder().setElement(
-                                                Element.newBuilder()
-                                                        .setStyleReferences(
-                                                                StyleIdsStack.newBuilder()
-                                                                        .addStyleIds(
-                                                                                templateStyleId))
-                                                        .setElementList(
-                                                                ElementList.getDefaultInstance())))
-                                        .addContents(Content.newBuilder().setElement(
-                                                Element.newBuilder()
-                                                        .setStyleReferences(
-                                                                StyleIdsStack.newBuilder()
-                                                                        .addStyleIds(frameStyleId))
-                                                        .setElementList(
-                                                                ElementList.getDefaultInstance())))
-                                        .addContents(Content.newBuilder().setElement(
-                                                Element.newBuilder()
-                                                        .setStyleReferences(
-                                                                StyleIdsStack.newBuilder()
-                                                                        .addStyleIds(globalStyleId))
-                                                        .setElementList(
-                                                                ElementList
-                                                                        .getDefaultInstance())))))
-                        .build();
-
-        PietSharedState pietSharedState =
-                PietSharedState.newBuilder()
-                        .addTemplates(template)
-                        .addStylesheets(Stylesheet.newBuilder().addStyles(globalStyle))
-                        .build();
-
-        // Frame defines style IDs that are also defined in the template
-        Frame frame = Frame.newBuilder()
-                              .setStylesheets(Stylesheets.newBuilder().addStylesheets(
-                                      Stylesheet.newBuilder()
-                                              .addStyles(frameStyle)
-                                              .addStyles(frameTemplateStyle)))
-                              .build();
-
-        // Set up a "real" frameContext, adapterParameters, factory
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        List<PietSharedState> pietSharedStates = Collections.singletonList(pietSharedState);
-        HostProviders mockHostProviders = mock(HostProviders.class);
-        AssetProvider mockAssetProvider = mock(AssetProvider.class);
-        when(mockHostProviders.getAssetProvider()).thenReturn(mockAssetProvider);
-        MediaQueryHelper mediaQueryHelper =
-                new MediaQueryHelper(FRAME_WIDTH_PX, mockAssetProvider, context);
-        PietStylesHelper pietStylesHelper =
-                new PietStylesHelperFactory().get(pietSharedStates, mediaQueryHelper);
-        mFrameContext = FrameContext.createFrameContext(frame, pietSharedStates, pietStylesHelper,
-                DebugBehavior.VERBOSE, new DebugLogger(), mActionHandler, mockHostProviders,
-                new FrameLayout(context));
-        AdapterParameters adapterParameters = new AdapterParameters(
-                context, Suppliers.of(null), mockHostProviders, new FakeClock(), false, false);
-        TemplateBinder templateBinder = adapterParameters.mTemplateBinder;
-
-        // Create and bind adapter
-        TemplateAdapterModel templateModel =
-                new TemplateAdapterModel(template, BindingContext.getDefaultInstance());
-        ElementAdapter<? extends View, ?> adapter =
-                templateBinder.createTemplateAdapter(templateModel, mFrameContext);
-        templateBinder.bindTemplateAdapter(adapter, templateModel, mFrameContext);
-
-        // Check views to ensure that template styles get set, but not frame or global styles.
-        LinearLayout templateList = (LinearLayout) adapter.getView();
-        assertThat(templateList.getChildCount()).isEqualTo(3);
-
-        // Template style element gets background from template
-        assertThat(((ColorDrawable) templateList.getChildAt(0).getBackground()).getColor())
-                .isEqualTo(templateColor);
-
-        // Frame style element gets no background - frame styles are not in scope for template.
-        assertThat(templateList.getChildAt(1).getBackground()).isNull();
-
-        // Global style element gets no background - global styles are not in scope for template.
-        assertThat(templateList.getChildAt(2).getBackground()).isNull();
-    }
-
-    @Test
-    public void testBindTemplateAdapter_success() {
-        // Set up data for test: template, shared states, binding context, template adapter model
-        String templateId = "papa";
-        Template template = Template.newBuilder()
-                                    .setTemplateId(templateId)
-                                    .setElement(Element.newBuilder().setElementList(
-                                            ElementList.getDefaultInstance()))
-                                    .build();
-        when(mFrameContext.getTemplate(templateId)).thenReturn(template);
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        List<PietSharedState> sharedStates = Collections.singletonList(sharedState);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("potato"))
-                        .build();
-        TemplateAdapterModel model = new TemplateAdapterModel(template, bindingContext);
-
-        // Set frameContext to return a new frameContext when a template is bound
-        FrameContext templateContext = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(template, bindingContext))
-                .thenReturn(templateContext);
-        doReturn(mElementListAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(DEFAULT_TEMPLATE_ELEMENT, templateContext);
-
-        // Create adapter and ensure template key is set.
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createTemplateAdapter(model, mFrameContext);
-        ArgumentCaptor<RecyclerKey> keyArgumentCaptor = ArgumentCaptor.forClass(RecyclerKey.class);
-        verify(mElementListAdapter).setKey(keyArgumentCaptor.capture());
-        when(mElementListAdapter.getKey()).thenReturn(keyArgumentCaptor.getValue());
-
-        mTemplateBinder.bindTemplateAdapter(adapter, model, mFrameContext);
-
-        // Assert that adapter is bound with the template frameContext
-        verify(mElementListAdapter).bindModel(model.getTemplate().getElement(), templateContext);
-    }
-
-    @Test
-    public void testBindTemplateAdapter_nullKey() {
-        TemplateAdapterModel templateAdapterModel =
-                new TemplateAdapterModel(Template.getDefaultInstance(), null);
-        when(mElementListAdapter.getKey()).thenReturn(null);
-
-        assertThatRunnable(()
-                                   -> mTemplateBinder.bindTemplateAdapter(mElementListAdapter,
-                                           templateAdapterModel, mFrameContext))
-                .throwsAnExceptionOfType(NullPointerException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Adapter key was null");
-    }
-
-    @Test
-    public void testBindTemplateAdapter_notATemplateAdapter() {
-        TemplateAdapterModel templateAdapterModel =
-                new TemplateAdapterModel(Template.getDefaultInstance(), null);
-        when(mElementListAdapter.getKey()).thenReturn(ElementListAdapter.KeySupplier.SINGLETON_KEY);
-
-        assertThatRunnable(()
-                                   -> mTemplateBinder.bindTemplateAdapter(mElementListAdapter,
-                                           templateAdapterModel, mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("bindTemplateAdapter only applicable for template adapters");
-    }
-
-    @Test
-    public void testBindTemplateAdapter_templateMismatch() {
-        // Set up data for test: two templates, shared states, binding context, template adapter
-        // model
-        String templateId1 = "papa";
-        Template template1 = Template.newBuilder()
-                                     .setTemplateId(templateId1)
-                                     .setElement(DEFAULT_TEMPLATE_ELEMENT)
-                                     .build();
-        String templateId2 = "mama";
-        Template template2 =
-                Template.newBuilder()
-                        .setTemplateId(templateId2)
-                        .setElement(Element.newBuilder().setGridRow(GridRow.getDefaultInstance()))
-                        .build();
-        when(mFrameContext.getTemplate(templateId1)).thenReturn(template1);
-        when(mFrameContext.getTemplate(templateId2)).thenReturn(template2);
-        PietSharedState sharedState = PietSharedState.newBuilder()
-                                              .addTemplates(template1)
-                                              .addTemplates(template2)
-                                              .build();
-        List<PietSharedState> sharedStates = Collections.singletonList(sharedState);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("potato"))
-                        .build();
-        TemplateAdapterModel model1 = new TemplateAdapterModel(template1, bindingContext);
-        TemplateAdapterModel model2 = new TemplateAdapterModel(template2, bindingContext);
-        assertThat(new TemplateKey(template1, sharedStates, new ArrayList<>()))
-                .isNotEqualTo(new TemplateKey(template2, sharedStates, new ArrayList<>()));
-
-        // Set frameContext to return a new frameContext when a template is bound
-        FrameContext templateContext = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(template1, bindingContext))
-                .thenReturn(templateContext);
-        when(mFrameContext.createTemplateContext(template2, bindingContext))
-                .thenReturn(templateContext);
-        doReturn(mElementListAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(DEFAULT_TEMPLATE_ELEMENT, templateContext);
-
-        // Create adapter with first template and ensure template key is set.
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createTemplateAdapter(model1, mFrameContext);
-        ArgumentCaptor<RecyclerKey> keyArgumentCaptor = ArgumentCaptor.forClass(RecyclerKey.class);
-        verify(mElementListAdapter).setKey(keyArgumentCaptor.capture());
-        when(mElementListAdapter.getKey()).thenReturn(keyArgumentCaptor.getValue());
-
-        // Try to bind with a different template and fail.
-        assertThatRunnable(
-                () -> mTemplateBinder.bindTemplateAdapter(adapter, model2, mFrameContext))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Template keys did not match");
-    }
-
-    @Test
-    public void testCreateAndBindTemplateAdapter_newAdapter() {
-        // Set up data for test: template, shared states, binding context, template adapter model
-        String templateId = "papa";
-        Template template = Template.newBuilder()
-                                    .setTemplateId(templateId)
-                                    .setElement(DEFAULT_TEMPLATE_ELEMENT)
-                                    .build();
-        when(mFrameContext.getTemplate(templateId)).thenReturn(template);
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        List<PietSharedState> sharedStates = Collections.singletonList(sharedState);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("potato"))
-                        .build();
-        TemplateAdapterModel model = new TemplateAdapterModel(template, bindingContext);
-
-        // Set frameContext to return a new frameContext when a template is bound
-        FrameContext templateContext = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(template, bindingContext))
-                .thenReturn(templateContext);
-        doReturn(mElementListAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(DEFAULT_TEMPLATE_ELEMENT, templateContext);
-
-        // Create adapter and ensure template key is set.
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createAndBindTemplateAdapter(model, mFrameContext);
-
-        assertThat(adapter).isSameInstanceAs(mElementListAdapter);
-        verify(mElementListAdapter)
-                .setKey(new TemplateKey(template, sharedStates, new ArrayList<>()));
-
-        // Assert that adapter is bound with the template frameContext
-        verify(mElementListAdapter).bindModel(model.getTemplate().getElement(), templateContext);
-    }
-
-    @Test
-    public void testCreateAndBindTemplateAdapter_recycled() {
-        // Set up data for test: template, shared states, binding context, template adapter model
-        String templateId = "papa";
-        Template template = Template.newBuilder()
-                                    .setTemplateId(templateId)
-                                    .setElement(DEFAULT_TEMPLATE_ELEMENT)
-                                    .build();
-        when(mFrameContext.getTemplate(templateId)).thenReturn(template);
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        List<PietSharedState> sharedStates = Collections.singletonList(sharedState);
-        when(mFrameContext.getPietSharedStates()).thenReturn(sharedStates);
-        BindingContext bindingContext =
-                BindingContext.newBuilder()
-                        .addBindingValues(BindingValue.newBuilder().setBindingId("potato"))
-                        .build();
-        TemplateAdapterModel model = new TemplateAdapterModel(template, bindingContext);
-
-        // Set frameContext to return a new frameContext when a template is bound
-        FrameContext templateContext = mock(FrameContext.class);
-        when(mFrameContext.createTemplateContext(template, bindingContext))
-                .thenReturn(templateContext);
-        doReturn(mElementListAdapter)
-                .when(mAdapterFactory)
-                .createAdapterForElement(DEFAULT_TEMPLATE_ELEMENT, templateContext);
-
-        TemplateKey templateKey = new TemplateKey(template, sharedStates, new ArrayList<>());
-        when(mElementListAdapter.getKey()).thenReturn(templateKey);
-        mTemplateRecyclerPool.put(templateKey, mElementListAdapter);
-
-        // Create adapter and ensure template key is set.
-        ElementAdapter<? extends View, ?> adapter =
-                mTemplateBinder.createAndBindTemplateAdapter(model, mFrameContext);
-        assertThat(adapter).isSameInstanceAs(mElementListAdapter);
-
-        // We don't get the adapter from the factory
-        verifyZeroInteractions(mAdapterFactory);
-        verify(mElementListAdapter, never()).setKey(any(TemplateKey.class));
-
-        // Assert that adapter is bound with the template frameContext
-        verify(mElementListAdapter).bindModel(model.getTemplate().getElement(), templateContext);
-    }
-
-    @Test
-    public void testTemplateKey_equalWithSameObjects() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        Stylesheet stylesheet = Stylesheet.newBuilder().setStylesheetId("S").build();
-        List<PietSharedState> sharedStates =
-                listOfSharedStates(PietSharedState.newBuilder().addTemplates(template).build());
-        List<Stylesheet> stylesheets = Collections.singletonList(stylesheet);
-        TemplateKey key1 = new TemplateKey(template, sharedStates, stylesheets);
-        TemplateKey key2 = new TemplateKey(template, sharedStates, stylesheets);
-
-        assertThat(key1.hashCode()).isEqualTo(key2.hashCode());
-        assertThat(key1).isEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_equalWithDifferentTemplateObject() {
-        Template template1 = Template.newBuilder().setTemplateId("T").build();
-        Template template2 = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template1).build();
-        TemplateKey key1 =
-                new TemplateKey(template1, listOfSharedStates(sharedState), new ArrayList<>());
-        TemplateKey key2 =
-                new TemplateKey(template2, listOfSharedStates(sharedState), new ArrayList<>());
-
-        assertThat(key1.hashCode()).isEqualTo(key2.hashCode());
-        assertThat(key1).isEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_equalWithDifferentSharedStateObject() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState1 = PietSharedState.newBuilder().addTemplates(template).build();
-        PietSharedState sharedState2 = PietSharedState.newBuilder().addTemplates(template).build();
-        TemplateKey key1 =
-                new TemplateKey(template, listOfSharedStates(sharedState1), new ArrayList<>());
-        TemplateKey key2 =
-                new TemplateKey(template, listOfSharedStates(sharedState2), new ArrayList<>());
-
-        assertThat(key1.hashCode()).isEqualTo(key2.hashCode());
-        assertThat(key1).isEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_equalWithDifferentStylesheetObject() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        Stylesheet stylesheet1 = Stylesheet.newBuilder().setStylesheetId("1").build();
-        Stylesheet stylesheet2 = Stylesheet.newBuilder().setStylesheetId("1").build();
-        TemplateKey key1 = new TemplateKey(
-                template, listOfSharedStates(sharedState), Collections.singletonList(stylesheet1));
-        TemplateKey key2 = new TemplateKey(
-                template, listOfSharedStates(sharedState), Collections.singletonList(stylesheet2));
-
-        assertThat(key1.hashCode()).isEqualTo(key2.hashCode());
-        assertThat(key1).isEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_differentWithDifferentLengthSharedStates() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState1 = PietSharedState.newBuilder().addTemplates(template).build();
-        TemplateKey key1 =
-                new TemplateKey(template, listOfSharedStates(sharedState1), new ArrayList<>());
-        TemplateKey key2 = new TemplateKey(
-                template, listOfSharedStates(sharedState1, sharedState1), new ArrayList<>());
-
-        assertThat(key1.hashCode()).isNotEqualTo(key2.hashCode());
-        assertThat(key1).isNotEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_differentWithDifferentTemplate() {
-        Template template1 = Template.newBuilder().setTemplateId("T1").build();
-        Template template2 = Template.newBuilder().setTemplateId("T2").build();
-        List<PietSharedState> sharedStates =
-                listOfSharedStates(PietSharedState.newBuilder().addTemplates(template1).build());
-        TemplateKey key1 = new TemplateKey(template1, sharedStates, new ArrayList<>());
-        TemplateKey key2 = new TemplateKey(template2, sharedStates, new ArrayList<>());
-
-        assertThat(key1.hashCode()).isNotEqualTo(key2.hashCode());
-        assertThat(key1).isNotEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_differentWithDifferentSharedState() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState1 = PietSharedState.newBuilder().addTemplates(template).build();
-        PietSharedState sharedState2 = PietSharedState.getDefaultInstance();
-        TemplateKey key1 =
-                new TemplateKey(template, listOfSharedStates(sharedState1), new ArrayList<>());
-        TemplateKey key2 =
-                new TemplateKey(template, listOfSharedStates(sharedState2), new ArrayList<>());
-
-        assertThat(key1.hashCode()).isNotEqualTo(key2.hashCode());
-        assertThat(key1).isNotEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateKey_differentWithDifferentStylesheet() {
-        Template template = Template.newBuilder().setTemplateId("T").build();
-        PietSharedState sharedState = PietSharedState.newBuilder().addTemplates(template).build();
-        Stylesheet stylesheet1 = Stylesheet.newBuilder().setStylesheetId("1").build();
-        Stylesheet stylesheet2 = Stylesheet.newBuilder().setStylesheetId("2").build();
-        TemplateKey key1 = new TemplateKey(
-                template, listOfSharedStates(sharedState), Collections.singletonList(stylesheet1));
-        TemplateKey key2 = new TemplateKey(
-                template, listOfSharedStates(sharedState), Collections.singletonList(stylesheet2));
-
-        assertThat(key1.hashCode()).isNotEqualTo(key2.hashCode());
-        assertThat(key1).isNotEqualTo(key2);
-    }
-
-    @Test
-    public void testTemplateAdapterModel_getters() {
-        TemplateAdapterModel model =
-                new TemplateAdapterModel(MODEL_TEMPLATE, MODEL_BINDING_CONTEXT);
-        assertThat(model.getTemplate()).isSameInstanceAs(MODEL_TEMPLATE);
-        assertThat(model.getBindingContext()).isSameInstanceAs(MODEL_BINDING_CONTEXT);
-    }
-
-    @Test
-    public void testTemplateAdapterModel_lookUpTemplate() {
-        ActionHandler actionHandler = mock(ActionHandler.class);
-        List<PietSharedState> pietSharedStates = Collections.emptyList();
-        Context context = Robolectric.buildActivity(Activity.class).get();
-
-        HostProviders mockHostProviders = mock(HostProviders.class);
-        AssetProvider mockAssetProvider = mock(AssetProvider.class);
-        when(mockHostProviders.getAssetProvider()).thenReturn(mockAssetProvider);
-
-        MediaQueryHelper mediaQueryHelper =
-                new MediaQueryHelper(FRAME_WIDTH_PX, mockAssetProvider, context);
-        PietStylesHelper pietStylesHelper =
-                new PietStylesHelperFactory().get(pietSharedStates, mediaQueryHelper);
-
-        FrameContext frameContext = FrameContext.createFrameContext(
-                DEFAULT_FRAME, // This defines MODEL_TEMPLATE
-                pietSharedStates, pietStylesHelper, DebugBehavior.VERBOSE, new DebugLogger(),
-                actionHandler, mockHostProviders, new FrameLayout(context));
-
-        TemplateAdapterModel model = new TemplateAdapterModel(
-                MODEL_TEMPLATE.getTemplateId(), frameContext, MODEL_BINDING_CONTEXT);
-        assertThat(model.getTemplate()).isSameInstanceAs(MODEL_TEMPLATE);
-        assertThat(model.getBindingContext()).isSameInstanceAs(MODEL_BINDING_CONTEXT);
-    }
-
-    @Test
-    public void testTemplateAdapterModel_equalsSame() {
-        TemplateAdapterModel model1 =
-                new TemplateAdapterModel(MODEL_TEMPLATE, MODEL_BINDING_CONTEXT);
-        TemplateAdapterModel model2 =
-                new TemplateAdapterModel(MODEL_TEMPLATE, MODEL_BINDING_CONTEXT);
-        assertThat(model1).isEqualTo(model2);
-        assertThat(model1.hashCode()).isEqualTo(model2.hashCode());
-    }
-
-    @Test
-    public void testTemplateAdapterModel_equalsOtherInstance() {
-        TemplateAdapterModel model1 =
-                new TemplateAdapterModel(MODEL_TEMPLATE, MODEL_BINDING_CONTEXT);
-        TemplateAdapterModel model2 = new TemplateAdapterModel(
-                MODEL_TEMPLATE.toBuilder().build(), MODEL_BINDING_CONTEXT.toBuilder().build());
-        assertThat(model1.getTemplate()).isNotSameInstanceAs(model2.getTemplate());
-        assertThat(model1.getBindingContext()).isNotSameInstanceAs(model2.getBindingContext());
-        assertThat(model1).isEqualTo(model2);
-        assertThat(model1.hashCode()).isEqualTo(model2.hashCode());
-    }
-
-    @Test
-    public void testTemplateAdapterModel_notEquals() {
-        TemplateAdapterModel model1 =
-                new TemplateAdapterModel(MODEL_TEMPLATE, MODEL_BINDING_CONTEXT);
-        TemplateAdapterModel model2 = new TemplateAdapterModel(
-                MODEL_TEMPLATE.toBuilder().clearTemplateId().build(), MODEL_BINDING_CONTEXT);
-        TemplateAdapterModel model3 = new TemplateAdapterModel(
-                MODEL_TEMPLATE, MODEL_BINDING_CONTEXT.toBuilder().clearBindingValues().build());
-        assertThat(model1).isNotEqualTo(model2);
-        assertThat(model1.hashCode()).isNotEqualTo(model2.hashCode());
-        assertThat(model1).isNotEqualTo(model3);
-        assertThat(model1.hashCode()).isNotEqualTo(model3.hashCode());
-    }
-
-    private List<PietSharedState> listOfSharedStates(PietSharedState... pietSharedStates) {
-        List<PietSharedState> sharedStates = new ArrayList<>();
-        Collections.addAll(sharedStates, pietSharedStates);
-        return sharedStates;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TextElementAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TextElementAdapterTest.java
deleted file mode 100644
index 8fd5bd5f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TextElementAdapterTest.java
+++ /dev/null
@@ -1,557 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.text.Layout;
-import android.text.TextUtils.TruncateAt;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.chrome.browser.feed.library.piet.DebugLogger.MessageType;
-import org.chromium.chrome.browser.feed.library.piet.TextElementAdapter.TextElementKey;
-import org.chromium.chrome.browser.feed.library.piet.host.AssetProvider;
-import org.chromium.chrome.browser.feed.library.piet.host.TypefaceProvider.GoogleSansTypeface;
-import org.chromium.components.feed.core.proto.ui.piet.BindingRefsProto.StyleBindingRef;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElement;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.Element;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.TextElement;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Font;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Style;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.StyleIdsStack;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.TextAlignmentHorizontal;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.TextAlignmentVertical;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Typeface.CommonTypeface;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests of the {@link TextElementAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TextElementAdapterTest {
-    @Mock
-    private FrameContext mFrameContext;
-    @Mock
-    private StyleProvider mMockStyleProvider;
-    @Mock
-    private HostProviders mMockHostProviders;
-    @Mock
-    private AssetProvider mMockAssetProvider;
-
-    private AdapterParameters mAdapterParameters;
-
-    private Context mContext;
-
-    private TextElementAdapter mAdapter;
-    private int mEmptyTextElementLineHeight;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        when(mMockHostProviders.getAssetProvider()).thenReturn(mMockAssetProvider);
-        when(mMockAssetProvider.isRtL()).thenReturn(false);
-        when(mMockStyleProvider.getRoundedCorners())
-                .thenReturn(RoundedCorners.getDefaultInstance());
-        when(mMockStyleProvider.getTextAlignment()).thenReturn(Gravity.START | Gravity.TOP);
-
-        mAdapterParameters = new AdapterParameters(null, null, mMockHostProviders,
-                new ParameterizedTextEvaluator(new FakeClock()), null, null, new FakeClock());
-
-        TextElementAdapter adapterForEmptyElement =
-                new TestTextElementAdapter(mContext, mAdapterParameters);
-        // Get emptyTextElementHeight based on a text element with no content or styles set.
-        Element textElement = getBaseTextElement();
-        adapterForEmptyElement.createAdapter(textElement, mFrameContext);
-        mEmptyTextElementLineHeight = adapterForEmptyElement.getBaseView().getLineHeight();
-
-        mAdapter = new TestTextElementAdapter(mContext, mAdapterParameters);
-    }
-
-    @Test
-    public void testHyphenationDisabled() {
-        assertThat(mAdapter.getBaseView().getBreakStrategy())
-                .isEqualTo(Layout.BREAK_STRATEGY_SIMPLE);
-    }
-
-    @Test
-    public void testCreateAdapter_setsStyles() {
-        Element textElement = getBaseTextElement(mMockStyleProvider);
-        int color = Color.RED;
-        int maxLines = 72;
-        when(mMockStyleProvider.getFont()).thenReturn(Font.getDefaultInstance());
-        when(mMockStyleProvider.getColor()).thenReturn(color);
-        when(mMockStyleProvider.getMaxLines()).thenReturn(maxLines);
-
-        mAdapter.createAdapter(textElement, mFrameContext);
-
-        verify(mMockStyleProvider).applyElementStyles(mAdapter);
-        assertThat(mAdapter.getBaseView().getMaxLines()).isEqualTo(maxLines);
-        assertThat(mAdapter.getBaseView().getEllipsize()).isEqualTo(TruncateAt.END);
-        assertThat(mAdapter.getBaseView().getCurrentTextColor()).isEqualTo(color);
-    }
-
-    @Test
-    public void testSetFont_usesCommonFont() {
-        Font font =
-                Font.newBuilder()
-                        .addTypeface(StylesProto.Typeface.newBuilder().setCommonTypeface(
-                                CommonTypeface.PLATFORM_DEFAULT_MEDIUM))
-                        .addTypeface(StylesProto.Typeface.newBuilder().setCustomTypeface("notused"))
-                        .build();
-
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-
-        verify(mMockAssetProvider, never())
-                .getTypeface(anyString(), anyBoolean(), ArgumentMatchers.<Consumer<Typeface>>any());
-    }
-
-    @Test
-    public void testSetFont_callsHost() {
-        Font font = Font.newBuilder()
-                            .addTypeface(
-                                    StylesProto.Typeface.newBuilder().setCustomTypeface("goodfont"))
-                            .build();
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-
-        verify(mMockAssetProvider, atLeastOnce())
-                .getTypeface(eq("goodfont"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-    }
-
-    @Test
-    public void testSetFont_callsHostWithItalic() {
-        Font font =
-                Font.newBuilder()
-                        .addTypeface(
-                                StylesProto.Typeface.newBuilder().setCustomTypeface("goodfont"))
-                        .addTypeface(StylesProto.Typeface.newBuilder().setCustomTypeface("badfont"))
-                        .setItalic(true)
-                        .build();
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-
-        verify(mMockAssetProvider, atLeastOnce())
-                .getTypeface(eq("goodfont"), eq(true), ArgumentMatchers.<Consumer<Typeface>>any());
-    }
-
-    @Test
-    public void testSetFont_callsHostWithFallback() {
-        Font font =
-                Font.newBuilder()
-                        .addTypeface(StylesProto.Typeface.newBuilder().setCustomTypeface("badfont"))
-                        .addTypeface(
-                                StylesProto.Typeface.newBuilder().setCustomTypeface("goodfont"))
-                        .build();
-        TextElementKey key = new TextElementKey(font);
-        // Consumer accepts null for badfont
-        doAnswer(answer -> {
-            Consumer<Typeface> typefaceConsumer = answer.getArgument(2);
-            typefaceConsumer.accept(null);
-            return null;
-        })
-                .when(mMockAssetProvider)
-                .getTypeface(eq("badfont"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-        // Consumer accepts hosttypeface for goodfont
-        Typeface hostTypeface = Typeface.create("host", Typeface.BOLD_ITALIC);
-        doAnswer(answer -> {
-            Consumer<Typeface> typefaceConsumer = answer.getArgument(2);
-            typefaceConsumer.accept(hostTypeface);
-            return null;
-        })
-                .when(mMockAssetProvider)
-                .getTypeface(eq("goodfont"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-
-        Typeface typeface = mAdapter.getBaseView().getTypeface();
-        assertThat(typeface).isEqualTo(hostTypeface);
-        InOrder inOrder = inOrder(mMockAssetProvider);
-        inOrder.verify(mMockAssetProvider, atLeastOnce())
-                .getTypeface(eq("badfont"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-        inOrder.verify(mMockAssetProvider, atLeastOnce())
-                .getTypeface(eq("goodfont"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-    }
-
-    @Test
-    public void testSetFont_hostReturnsNull() {
-        Font font = Font.newBuilder()
-                            .addTypeface(
-                                    StylesProto.Typeface.newBuilder().setCustomTypeface("notvalid"))
-                            .build();
-        doAnswer(answer -> {
-            Consumer<Typeface> typefaceConsumer = answer.getArgument(2);
-            typefaceConsumer.accept(null);
-            return null;
-        })
-                .when(mMockAssetProvider)
-                .getTypeface(eq("notvalid"), eq(false), ArgumentMatchers.<Consumer<Typeface>>any());
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-        Typeface typeface = mAdapter.getBaseView().getTypeface();
-
-        verify(mFrameContext)
-                .reportMessage(MessageType.WARNING, ErrorCode.ERR_MISSING_FONTS,
-                        "Could not load specified typefaces.");
-        assertThat(typeface).isEqualTo(new TextView(mContext).getTypeface());
-    }
-
-    @Test
-    public void testSetFont_callsHostForGoogleSans() {
-        Font font = Font.newBuilder()
-                            .addTypeface(StylesProto.Typeface.newBuilder().setCommonTypeface(
-                                    CommonTypeface.GOOGLE_SANS_MEDIUM))
-                            .build();
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-
-        verify(mMockAssetProvider, atLeastOnce())
-                .getTypeface(eq(GoogleSansTypeface.GOOGLE_SANS_MEDIUM), eq(false),
-                        ArgumentMatchers.<Consumer<Typeface>>any());
-    }
-
-    @Test
-    public void testSetFont_italics() {
-        Font font = Font.newBuilder().setItalic(true).build();
-        TextElementKey key = new TextElementKey(font);
-
-        mAdapter.setValuesUsedInRecyclerKey(key, mFrameContext);
-        Typeface typeface = mAdapter.getBaseView().getTypeface();
-        // Typeface.isBold and Typeface.isItalic don't work properly in roboelectric.
-        assertThat(typeface.getStyle() & Typeface.BOLD).isEqualTo(0);
-        assertThat(typeface.getStyle() & Typeface.ITALIC).isGreaterThan(0);
-    }
-
-    @Test
-    public void testGoogleSansEnumToStringDef() {
-        assertThat(TextElementAdapter.googleSansEnumToStringDef(CommonTypeface.GOOGLE_SANS_REGULAR))
-                .isEqualTo(GoogleSansTypeface.GOOGLE_SANS_REGULAR);
-        assertThat(TextElementAdapter.googleSansEnumToStringDef(CommonTypeface.GOOGLE_SANS_MEDIUM))
-                .isEqualTo(GoogleSansTypeface.GOOGLE_SANS_MEDIUM);
-        assertThat(TextElementAdapter.googleSansEnumToStringDef(
-                           CommonTypeface.PLATFORM_DEFAULT_MEDIUM))
-                .isEqualTo(GoogleSansTypeface.UNDEFINED);
-    }
-
-    @Test
-    public void testSetLineHeight() {
-        int lineHeightToSetSp = 18;
-        Style lineHeightStyle1 =
-                Style.newBuilder()
-                        .setFont(Font.newBuilder().setLineHeight(lineHeightToSetSp))
-                        .build();
-        StyleProvider styleProvider1 = new StyleProvider(lineHeightStyle1, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider1);
-
-        mAdapter.createAdapter(textElement, mFrameContext);
-        TextView textView = mAdapter.getBaseView();
-        float actualLineHeightPx = textView.getLineHeight();
-        int actualLineHeightSp =
-                (int) LayoutUtils.pxToSp(actualLineHeightPx, textView.getContext());
-        assertThat(actualLineHeightSp).isEqualTo(lineHeightToSetSp);
-    }
-
-    @Test
-    public void testGetExtraLineHeight_roundDown() {
-        // Extra height is 40.2px. This gets rounded down between the lines (to 40) and rounded up
-        // for top and bottom padding (for 21 + 20 = 41).
-        initializeAdapterWithExtraLineHeightPx(40.2f);
-
-        TextElementAdapter.ExtraLineHeight extraLineHeight = mAdapter.getExtraLineHeight();
-
-        assertThat(extraLineHeight.betweenLinesExtraPx()).isEqualTo(40);
-        assertThat(extraLineHeight.bottomPaddingPx()).isEqualTo(21);
-        assertThat(extraLineHeight.topPaddingPx()).isEqualTo(20);
-    }
-
-    @Test
-    public void testGetExtraLineHeight_noRound() {
-        // Extra height is 40px. 40 pixels will be added between each line, and that amount is split
-        // (20 and 20) to be added to the top and bottom of the text element.
-        initializeAdapterWithExtraLineHeightPx(40.0f);
-
-        TextElementAdapter.ExtraLineHeight extraLineHeight = mAdapter.getExtraLineHeight();
-
-        assertThat(extraLineHeight.betweenLinesExtraPx()).isEqualTo(40);
-        assertThat(extraLineHeight.bottomPaddingPx()).isEqualTo(20);
-        assertThat(extraLineHeight.topPaddingPx()).isEqualTo(20);
-    }
-
-    @Test
-    public void testGetExtraLineHeight_roundUp() {
-        // Extra height is 40.8px. This gets rounded up between the lines (to 41) and rounded down
-        // for top and bottom padding (for 20 + 20 = 40).
-        initializeAdapterWithExtraLineHeightPx(40.8f);
-
-        TextElementAdapter.ExtraLineHeight extraLineHeight = mAdapter.getExtraLineHeight();
-
-        assertThat(extraLineHeight.betweenLinesExtraPx()).isEqualTo(41);
-        assertThat(extraLineHeight.bottomPaddingPx()).isEqualTo(20);
-        assertThat(extraLineHeight.topPaddingPx()).isEqualTo(20);
-    }
-
-    private void initializeAdapterWithExtraLineHeightPx(float lineHeightPx) {
-        // Line height is specified in sp, so line height px = scaledDensity  x line height sp
-        // These tests set display density because, in order to test the rounding behavior of
-        // extraLineHeight, we need a lineHeight integer (in sp) that results in a decimal value in
-        // px.
-        int lineHeightSp = 10;
-        float totalLineHeightPx = mEmptyTextElementLineHeight + lineHeightPx;
-        mContext.getResources().getDisplayMetrics().scaledDensity =
-                totalLineHeightPx / lineHeightSp;
-        Style lineHeightStyle1 =
-                Style.newBuilder().setFont(Font.newBuilder().setLineHeight(lineHeightSp)).build();
-        StyleProvider styleProvider1 = new StyleProvider(lineHeightStyle1, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider1);
-        mAdapter.createAdapter(textElement, mFrameContext);
-    }
-
-    @Test
-    public void testBind_setsTextAlignment_horizontal() {
-        Style style =
-                Style.newBuilder()
-                        .setTextAlignmentHorizontal(TextAlignmentHorizontal.TEXT_ALIGNMENT_CENTER)
-                        .build();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getGravity())
-                .isEqualTo(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
-    }
-
-    @Test
-    public void testBind_setsTextAlignment_vertical() {
-        Style style = Style.newBuilder()
-                              .setTextAlignmentVertical(TextAlignmentVertical.TEXT_ALIGNMENT_BOTTOM)
-                              .build();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getGravity()).isEqualTo(Gravity.START | Gravity.BOTTOM);
-    }
-
-    @Test
-    public void testBind_setsTextAlignment_both() {
-        Style style =
-                Style.newBuilder()
-                        .setTextAlignmentHorizontal(TextAlignmentHorizontal.TEXT_ALIGNMENT_END)
-                        .setTextAlignmentVertical(TextAlignmentVertical.TEXT_ALIGNMENT_MIDDLE)
-                        .build();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getGravity())
-                .isEqualTo(Gravity.END | Gravity.CENTER_VERTICAL);
-    }
-
-    @Test
-    public void testBind_setsTextAlignment_default() {
-        Style style = Style.getDefaultInstance();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-        mAdapter.getBaseView().setGravity(Gravity.BOTTOM | Gravity.RIGHT);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getGravity()).isEqualTo(Gravity.START | Gravity.TOP);
-    }
-
-    @Test
-    public void testBind_setsStylesOnlyIfBindingIsDefined() {
-        int maxLines = 2;
-        Style style = Style.newBuilder().setMaxLines(maxLines).build();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-        assertThat(mAdapter.getBaseView().getMaxLines()).isEqualTo(maxLines);
-
-        // Styles should not change on a re-bind
-        mAdapter.unbindModel();
-        StyleIdsStack otherStyle = StyleIdsStack.newBuilder().addStyleIds("ignored").build();
-        textElement = getBaseTextElement().toBuilder().setStyleReferences(otherStyle).build();
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        assertThat(mAdapter.getBaseView().getMaxLines()).isEqualTo(maxLines);
-        verify(mFrameContext, never()).makeStyleFor(otherStyle);
-
-        // Styles only change if new model has style bindings
-        mAdapter.unbindModel();
-        StyleIdsStack otherStyleWithBinding =
-                StyleIdsStack.newBuilder()
-                        .setStyleBinding(StyleBindingRef.newBuilder().setBindingId("prionailurus"))
-                        .build();
-        textElement =
-                getBaseTextElement().toBuilder().setStyleReferences(otherStyleWithBinding).build();
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        verify(mFrameContext).makeStyleFor(otherStyleWithBinding);
-    }
-
-    @Test
-    public void bindWithUpdatedDensity_shouldUpdateLineHeight() {
-        final int lineHeightInTextElement = 50;
-        mContext.getResources().getDisplayMetrics().scaledDensity = 1;
-        Style style = Style.newBuilder()
-                              .setFont(Font.newBuilder().setLineHeight(lineHeightInTextElement))
-                              .build();
-        StyleProvider styleProvider = new StyleProvider(style, mMockAssetProvider);
-        Element textElement = getBaseTextElement(styleProvider);
-
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-        assertThat(mAdapter.getBaseView().getLineHeight()).isEqualTo(lineHeightInTextElement);
-
-        mAdapter.unbindModel();
-        // Change line height by changing the scale density
-        mContext.getResources().getDisplayMetrics().scaledDensity = 2;
-        mAdapter.bindModel(textElement, mFrameContext);
-        // getLineHeight() still returns pixels. The number of pixels should have been updated to
-        // reflect the new density.
-        assertThat(mAdapter.getBaseView().getLineHeight()).isEqualTo(lineHeightInTextElement * 2);
-
-        mAdapter.unbindModel();
-        // Make sure the line height is updated again when the scale density is changed back.
-        mContext.getResources().getDisplayMetrics().scaledDensity = 1;
-        mAdapter.bindModel(textElement, mFrameContext);
-        assertThat(mAdapter.getBaseView().getLineHeight()).isEqualTo(lineHeightInTextElement);
-    }
-
-    @Test
-    public void testUnbind() {
-        Element textElement = getBaseTextElement(null);
-        mAdapter.createAdapter(textElement, mFrameContext);
-        mAdapter.bindModel(textElement, mFrameContext);
-
-        TextView adapterView = mAdapter.getBaseView();
-        adapterView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
-        adapterView.setText("OLD TEXT");
-
-        mAdapter.unbindModel();
-
-        assertThat(mAdapter.getBaseView()).isSameInstanceAs(adapterView);
-        assertThat(adapterView.getTextAlignment()).isEqualTo(View.TEXT_ALIGNMENT_GRAVITY);
-        assertThat(adapterView.getText().toString()).isEmpty();
-    }
-
-    @Test
-    public void testGetStyles() {
-        StyleIdsStack elementStyles = StyleIdsStack.newBuilder().addStyleIds("hair").build();
-        when(mMockStyleProvider.getFont()).thenReturn(Font.getDefaultInstance());
-        Element textElement = getBaseTextElement(mMockStyleProvider)
-                                      .toBuilder()
-                                      .setStyleReferences(elementStyles)
-                                      .build();
-
-        mAdapter.createAdapter(textElement, mFrameContext);
-
-        assertThat(mAdapter.getElementStyleIdsStack()).isSameInstanceAs(elementStyles);
-    }
-
-    @Test
-    public void testGetModelFromElement() {
-        TextElement model =
-                TextElement.newBuilder()
-                        .setParameterizedText(ParameterizedText.newBuilder().setText("text"))
-                        .build();
-
-        Element elementWithModel =
-                Element.newBuilder()
-                        .setStyleReferences(StyleIdsStack.newBuilder().addStyleIds("spacer"))
-                        .setTextElement(model)
-                        .build();
-        assertThat(mAdapter.getModelFromElement(elementWithModel))
-                .isSameInstanceAs(elementWithModel);
-
-        Element elementWithWrongModel =
-                Element.newBuilder().setCustomElement(CustomElement.getDefaultInstance()).build();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(elementWithWrongModel))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing TextElement");
-
-        Element emptyElement = Element.getDefaultInstance();
-        assertThatRunnable(() -> mAdapter.getModelFromElement(emptyElement))
-                .throwsAnExceptionOfType(PietFatalException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Missing TextElement");
-    }
-
-    private Element getBaseTextElement() {
-        return getBaseTextElement(null);
-    }
-
-    private Element getBaseTextElement(/*@Nullable*/ StyleProvider styleProvider) {
-        StyleProvider sp =
-                styleProvider != null ? styleProvider : mAdapterParameters.mDefaultStyleProvider;
-        when(mFrameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(sp);
-
-        return Element.newBuilder().setTextElement(TextElement.getDefaultInstance()).build();
-    }
-
-    private static class TestTextElementAdapter extends TextElementAdapter {
-        TestTextElementAdapter(Context mContext, AdapterParameters parameters) {
-            super(mContext, parameters);
-        }
-
-        @Override
-        void setTextOnView(FrameContext mFrameContext, TextElement textElement) {}
-
-        @Override
-        TextElementKey createKey(Font font) {
-            return new TextElementKey(font);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ViewUtilsTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ViewUtilsTest.java
deleted file mode 100644
index 2839761..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ViewUtilsTest.java
+++ /dev/null
@@ -1,469 +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.
-
-package org.chromium.chrome.browser.feed.library.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.testing.shadows.ExtendedShadowView;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Actions;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.VisibilityAction;
-import org.chromium.components.feed.core.proto.ui.piet.LogDataProto.LogData;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** Tests of the {@link ViewUtils}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ExtendedShadowView.class})
-public class ViewUtilsTest {
-    private final Context mContext = Robolectric.buildActivity(Activity.class).get();
-
-    private static final Frame DEFAULT_FRAME = Frame.newBuilder().setTag("Frame").build();
-    private static final Action DEFAULT_ACTION = Action.getDefaultInstance();
-    private static final Actions DEFAULT_ACTIONS =
-            Actions.newBuilder().setOnClickAction(DEFAULT_ACTION).build();
-    private static final Actions LONG_CLICK_ACTIONS =
-            Actions.newBuilder().setOnLongClickAction(DEFAULT_ACTION).build();
-    private static final Action PARTIAL_VIEW_ACTION = Action.newBuilder().build();
-    private static final Action FULL_VIEW_ACTION = Action.newBuilder().build();
-    private static final Actions VIEW_ACTIONS =
-            Actions.newBuilder()
-                    .addOnViewActions(
-                            VisibilityAction.newBuilder().setProportionVisible(0.01f).setAction(
-                                    PARTIAL_VIEW_ACTION))
-                    .addOnViewActions(
-                            VisibilityAction.newBuilder().setProportionVisible(1.00f).setAction(
-                                    FULL_VIEW_ACTION))
-                    .build();
-
-    // Triggers when more than 1% of the view is visible
-    private static final VisibilityAction VIEW_ACTION =
-            VisibilityAction.newBuilder()
-                    .setProportionVisible(0.01f)
-                    .setAction(Action.newBuilder().build())
-                    .build();
-    // Triggers when more than 1% of the view is hidden
-    private static final VisibilityAction HIDE_ACTION =
-            VisibilityAction.newBuilder()
-                    .setProportionVisible(0.99f)
-                    .setAction(Action.newBuilder().build())
-                    .build();
-    private static final Actions VIEW_AND_HIDE_ACTIONS = Actions.newBuilder()
-                                                                 .addOnViewActions(VIEW_ACTION)
-                                                                 .addOnHideActions(HIDE_ACTION)
-                                                                 .build();
-
-    @Mock
-    private ActionHandler mMockActionHandler;
-    @Mock
-    private FrameContext mMockFrameContext;
-    @Mock
-    private View.OnClickListener mMockListener;
-    @Mock
-    private View.OnLongClickListener mMockLongClickListener;
-
-    private final View mView = new View(mContext);
-    private final View mViewport = new View(mContext);
-
-    private final ExtendedShadowView mViewShadow = Shadow.extract(mView);
-    private final ExtendedShadowView mViewportShadow = Shadow.extract(mViewport);
-
-    private final Set<VisibilityAction> mActiveActions = new HashSet<>();
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        when(mMockFrameContext.getFrame()).thenReturn(DEFAULT_FRAME);
-        when(mMockFrameContext.getActionHandler()).thenReturn(mMockActionHandler);
-    }
-
-    @Test
-    public void testSetOnClickActions_success() {
-        LogData logData = LogData.newBuilder().build();
-        ViewUtils.setOnClickActions(DEFAULT_ACTIONS, mView, mMockFrameContext, logData);
-
-        assertThat(mView.hasOnClickListeners()).isTrue();
-
-        mView.callOnClick();
-        verify(mMockActionHandler)
-                .handleAction(eq(DEFAULT_ACTION), eq(ActionType.CLICK), eq(DEFAULT_FRAME),
-                        eq(mView), same(logData));
-        assertThat(mView.getForeground()).isInstanceOf(RippleDrawable.class);
-    }
-
-    @Test
-    public void testSetOnLongClickActions_success() {
-        LogData logData = LogData.newBuilder().build();
-        ViewUtils.setOnClickActions(LONG_CLICK_ACTIONS, mView, mMockFrameContext, logData);
-
-        mView.performLongClick();
-        verify(mMockActionHandler)
-                .handleAction(eq(DEFAULT_ACTION), eq(ActionType.LONG_CLICK), eq(DEFAULT_FRAME),
-                        eq(mView), same(logData));
-        assertThat(mView.getForeground()).isInstanceOf(RippleDrawable.class);
-    }
-
-    @Test
-    public void testSetOnClickActions_noOnClickActionsDefinedClearsActions() {
-        mView.setOnClickListener(mMockListener);
-        assertThat(mView.hasOnClickListeners()).isTrue();
-
-        ViewUtils.setOnClickActions(Actions.getDefaultInstance(), mView, mMockFrameContext,
-                LogData.getDefaultInstance());
-
-        assertViewNotClickable();
-        assertThat(mView.getForeground()).isNull();
-    }
-
-    @Test
-    public void testSetOnClickActions_noOnLongClickActionsDefinedClearsActions() {
-        mView.setOnLongClickListener(mMockLongClickListener);
-        assertThat(mView.isLongClickable()).isTrue();
-
-        ViewUtils.setOnClickActions(Actions.getDefaultInstance(), mView, mMockFrameContext,
-                LogData.getDefaultInstance());
-
-        assertThat(mView.isLongClickable()).isFalse();
-        assertThat(mView.getForeground()).isNull();
-    }
-
-    @Test
-    public void testClearOnClickActions_success() {
-        mView.setOnClickListener(mMockListener);
-        assertThat(mView.hasOnClickListeners()).isTrue();
-
-        ViewUtils.clearOnClickActions(mView);
-
-        assertViewNotClickable();
-    }
-
-    @Test
-    public void testClearOnLongClickActions_success() {
-        mView.setOnLongClickListener(mMockLongClickListener);
-        assertThat(mView.isLongClickable()).isTrue();
-
-        ViewUtils.clearOnLongClickActions(mView);
-
-        assertThat(mView.isLongClickable()).isFalse();
-    }
-
-    @Test
-    public void testSetAndClearClickActions() {
-        ViewUtils.setOnClickActions(Actions.newBuilder()
-                                            .setOnClickAction(DEFAULT_ACTION)
-                                            .setOnLongClickAction(DEFAULT_ACTION)
-                                            .build(),
-                mView, mMockFrameContext, LogData.getDefaultInstance());
-        ViewUtils.setOnClickActions(Actions.getDefaultInstance(), mView, mMockFrameContext,
-                LogData.getDefaultInstance());
-
-        assertViewNotClickable();
-        assertThat(mView.isLongClickable()).isFalse();
-        assertThat(mView.getForeground()).isNull();
-    }
-
-    @Test
-    public void testViewActions_notVisible() {
-        setupFullViewScenario();
-        mView.setVisibility(View.INVISIBLE);
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_notAttached() {
-        setupFullViewScenario();
-        mViewShadow.setAttachedToWindow(false);
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_notIntersecting() {
-        setupFullViewScenario();
-        mViewportShadow.setLocationOnScreen(0, 0);
-        mViewportShadow.setHeight(100);
-        mViewportShadow.setWidth(100);
-        mViewShadow.setLocationOnScreen(1000, 1000);
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_intersectionTriggersPartialView() {
-        setupPartialViewScenario();
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(PARTIAL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_fullyOverlappingTriggersFullViewAndPartialView() {
-        setupFullViewScenario();
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(FULL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-        verify(mMockActionHandler)
-                .handleAction(same(PARTIAL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_fullOverlapTriggersActions() {
-        setupFullViewScenario();
-        mViewShadow.setLocationOnScreen(0, 0);
-        mViewShadow.setWidth(100);
-        mViewShadow.setHeight(100);
-        mViewportShadow.setLocationOnScreen(0, 0);
-        mViewportShadow.setWidth(100);
-        mViewportShadow.setHeight(100);
-
-        ViewUtils.maybeTriggerViewActions(
-                mView, mViewport, VIEW_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(FULL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-        verify(mMockActionHandler)
-                .handleAction(same(PARTIAL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_noPartialViewAction() {
-        setupFullViewScenario();
-        ViewUtils.maybeTriggerViewActions(mView, mViewport,
-                Actions.newBuilder()
-                        .addOnViewActions(
-                                VisibilityAction.newBuilder().setProportionVisible(1.00f).setAction(
-                                        FULL_VIEW_ACTION))
-                        .build(),
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(FULL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_noFullViewAction() {
-        setupFullViewScenario();
-        ViewUtils.maybeTriggerViewActions(mView, mViewport,
-                Actions.newBuilder()
-                        .addOnViewActions(
-                                VisibilityAction.newBuilder().setProportionVisible(0.01f).setAction(
-                                        PARTIAL_VIEW_ACTION))
-                        .build(),
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(PARTIAL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_hideActionsNotTriggered() {
-        setupFullViewScenario();
-        ViewUtils.maybeTriggerViewActions(mView, mViewport,
-                Actions.newBuilder()
-                        .addOnHideActions(
-                                VisibilityAction.newBuilder().setProportionVisible(0.01f).setAction(
-                                        PARTIAL_VIEW_ACTION))
-                        .build(),
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_hideActionsTriggered() {
-        setupPartialViewScenario();
-        ViewUtils.maybeTriggerViewActions(mView, mViewport,
-                Actions.newBuilder()
-                        .addOnHideActions(
-                                VisibilityAction.newBuilder().setProportionVisible(0.90f).setAction(
-                                        PARTIAL_VIEW_ACTION))
-                        .build(),
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(same(PARTIAL_VIEW_ACTION), eq(ActionType.VIEW), same(DEFAULT_FRAME),
-                        same(mView), eq(LogData.getDefaultInstance()));
-    }
-
-    @Test
-    public void testViewActions_activeActionsPreventsTriggering_notVisible() {
-        mActiveActions.add(VIEW_ACTION);
-        mActiveActions.add(HIDE_ACTION);
-
-        setupFullViewScenario();
-        mViewShadow.setLocationOnScreen(1000, 1000);
-        ViewUtils.maybeTriggerViewActions(mView, mViewport, VIEW_AND_HIDE_ACTIONS,
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_activeActionsPreventsTriggering_partiallyVisible() {
-        setupPartialViewScenario();
-        mActiveActions.add(VIEW_ACTION);
-        mActiveActions.add(HIDE_ACTION);
-
-        ViewUtils.maybeTriggerViewActions(mView, mViewport, VIEW_AND_HIDE_ACTIONS,
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_activeActionsPreventsTriggering_fullyVisible() {
-        setupFullViewScenario();
-        mActiveActions.add(VIEW_ACTION);
-        mActiveActions.add(HIDE_ACTION);
-
-        ViewUtils.maybeTriggerViewActions(mView, mViewport, VIEW_AND_HIDE_ACTIONS,
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verifyZeroInteractions(mMockActionHandler);
-    }
-
-    @Test
-    public void testViewActions_notAttachedUnsetsActiveActions() {
-        setupFullViewScenario();
-        mViewShadow.setAttachedToWindow(false);
-        mActiveActions.add(VIEW_ACTION);
-
-        ViewUtils.maybeTriggerViewActions(mView, mViewport, VIEW_AND_HIDE_ACTIONS,
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        assertThat(mActiveActions).containsExactly(HIDE_ACTION);
-    }
-
-    @Test
-    public void testViewActions_notVisibleUnsetsActiveActions() {
-        setupFullViewScenario();
-        mView.setVisibility(View.INVISIBLE);
-        mActiveActions.add(VIEW_ACTION);
-
-        ViewUtils.maybeTriggerViewActions(mView, mViewport, VIEW_AND_HIDE_ACTIONS,
-                mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        assertThat(mActiveActions).containsExactly(HIDE_ACTION);
-    }
-
-    @Test
-    public void testHideActions() {
-        ViewUtils.triggerHideActions(
-                mView, VIEW_AND_HIDE_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        assertThat(mActiveActions).containsExactly(HIDE_ACTION);
-        verify(mMockActionHandler)
-                .handleAction(HIDE_ACTION.getAction(), ActionType.VIEW, DEFAULT_FRAME, mView,
-                        LogData.getDefaultInstance());
-    }
-
-    @Test
-    public void testHideActions_deduplicates() {
-        ViewUtils.triggerHideActions(
-                mView, VIEW_AND_HIDE_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler)
-                .handleAction(HIDE_ACTION.getAction(), ActionType.VIEW, DEFAULT_FRAME, mView,
-                        LogData.getDefaultInstance());
-
-        assertThat(mActiveActions).containsExactly(HIDE_ACTION);
-
-        // Hide action is not triggered again.
-        ViewUtils.triggerHideActions(
-                mView, VIEW_AND_HIDE_ACTIONS, mMockActionHandler, DEFAULT_FRAME, mActiveActions);
-        verify(mMockActionHandler, times(1))
-                .handleAction(HIDE_ACTION.getAction(), ActionType.VIEW, DEFAULT_FRAME, mView,
-                        LogData.getDefaultInstance());
-    }
-
-    @Test
-    public void testApplyOverlayColor_setsColorFilter() {
-        int overlayColor1 = 0xFFEEDDCC;
-        int overlayColor2 = 0xCCDDEEFF;
-        Drawable original =
-                new BitmapDrawable(Bitmap.createBitmap(12, 34, Bitmap.Config.ARGB_8888));
-
-        Drawable result1 = ViewUtils.applyOverlayColor(original, overlayColor1);
-        Drawable result2 = ViewUtils.applyOverlayColor(original, overlayColor2);
-
-        assertThat(result1).isNotSameInstanceAs(original);
-        assertThat(result1.getColorFilter())
-                .isEqualTo(new PorterDuffColorFilter(overlayColor1, Mode.SRC_IN));
-
-        assertThat(result2).isNotSameInstanceAs(original);
-        assertThat(result2.getColorFilter())
-                .isEqualTo(new PorterDuffColorFilter(overlayColor2, Mode.SRC_IN));
-    }
-
-    @Test
-    public void testApplyOverlayColor_nullIsNoOp() {
-        Drawable original =
-                new BitmapDrawable(Bitmap.createBitmap(12, 34, Bitmap.Config.ARGB_8888));
-
-        Drawable result1 = ViewUtils.applyOverlayColor(original, null);
-
-        assertThat(result1).isSameInstanceAs(original);
-        assertThat(result1.getColorFilter()).isNull();
-    }
-
-    /** Sets up view and viewport so that view should be fully visible. */
-    private void setupFullViewScenario() {
-        mView.setVisibility(View.VISIBLE);
-        mViewShadow.setAttachedToWindow(true);
-        mViewShadow.setLocationOnScreen(10, 10);
-        mViewShadow.setWidth(10);
-        mViewShadow.setHeight(10);
-
-        mViewport.setVisibility(View.VISIBLE);
-        mViewportShadow.setAttachedToWindow(true);
-        mViewportShadow.setLocationOnScreen(0, 0);
-        mViewportShadow.setWidth(100);
-        mViewportShadow.setHeight(100);
-
-        mActiveActions.clear();
-    }
-
-    private void setupPartialViewScenario() {
-        setupFullViewScenario();
-        mViewShadow.setHeight(1000);
-    }
-
-    private void assertViewNotClickable() {
-        assertThat(mView.hasOnClickListeners()).isFalse();
-        assertThat(mView.isClickable()).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/host/AssetProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/host/AssetProviderTest.java
deleted file mode 100644
index d120bfc0..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/host/AssetProviderTest.java
+++ /dev/null
@@ -1,95 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.host;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.widget.ImageView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link AssetProvider}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class AssetProviderTest {
-    @Mock
-    ImageLoader mImageLoader;
-    @Mock
-    StringFormatter mStringFormatter;
-    @Mock
-    TypefaceProvider mTypefaceProvider;
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-    }
-
-    @Test
-    public void testBuilder_setsFields() {
-        AssetProvider assetProvider = new AssetProvider(mImageLoader, mStringFormatter,
-                Suppliers.of(123), Suppliers.of(456L), Suppliers.of(true), Suppliers.of(true),
-                mTypefaceProvider);
-
-        assertThat(assetProvider.mImageLoader).isSameInstanceAs(mImageLoader);
-        assertThat(assetProvider.mStringFormatter).isSameInstanceAs(mStringFormatter);
-        assertThat(assetProvider.mTypefaceProvider).isSameInstanceAs(mTypefaceProvider);
-        assertThat(assetProvider.getDefaultCornerRadius()).isEqualTo(123);
-        assertThat(assetProvider.getFadeImageThresholdMs()).isEqualTo(456);
-        assertThat(assetProvider.isDarkTheme()).isTrue();
-        assertThat(assetProvider.isRtL()).isTrue();
-    }
-
-    @Test
-    public void testNullImageLoader() {
-        ImageLoader imageLoader = new NullImageLoader();
-        ImageView imageView = new ImageView(mContext);
-        imageView.setImageDrawable(new ColorDrawable(Color.RED));
-
-        imageLoader.getImage(
-                Image.getDefaultInstance(), 1, 2, drawable -> imageView.setImageDrawable(drawable));
-
-        assertThat(imageView.getDrawable()).isNull();
-    }
-
-    @Test
-    public void testNullTypefaceProvider() {
-        TypefaceProvider typefaceProvider = new NullTypefaceProvider();
-        final Object[] consumedObject = {""};
-
-        // Make sure the object isn't already null, or we'll get a false positive.
-        assertThat(consumedObject[0]).isNotNull();
-        // The consumer passed in just saves the value that is consumed, so we can check that it's
-        // null.
-        typefaceProvider.getTypeface(
-                "GOOGLE_SANS_MEDIUM", false, typeface -> consumedObject[0] = typeface);
-        assertThat(consumedObject[0]).isNull();
-    }
-
-    @Test
-    public void testEmptyStringFormatter() {
-        StringFormatter stringFormatter = new EmptyStringFormatter();
-
-        assertThat(stringFormatter.getRelativeElapsedString(123456)).isEmpty();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/AspectRatioScalingImageViewTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/AspectRatioScalingImageViewTest.java
deleted file mode 100644
index 45e061f..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/AspectRatioScalingImageViewTest.java
+++ /dev/null
@@ -1,230 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.View.MeasureSpec;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link AspectRatioScalingImageView}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class AspectRatioScalingImageViewTest {
-    private static final int DRAWABLE_WIDTH = 80;
-    private static final int DRAWABLE_HEIGHT = 40;
-    private static final int DRAWABLE_ASPECT_RATIO = 2;
-
-    private static final int CONTAINER_WIDTH = 90;
-    private static final int CONTAINER_HEIGHT = 30;
-
-    private AspectRatioScalingImageView mView;
-    private Drawable mDrawable;
-
-    @Before
-    public void setUp() {
-        mView = new AspectRatioScalingImageView(Robolectric.buildActivity(Activity.class).get());
-        mDrawable = new BitmapDrawable(
-                Bitmap.createBitmap(DRAWABLE_WIDTH, DRAWABLE_HEIGHT, Bitmap.Config.RGB_565));
-    }
-
-    @Test
-    public void testScaling_noDrawable_exactly() {
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.EXACTLY);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_noDrawable_unspecified() {
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(0);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(0);
-    }
-
-    @Test
-    public void testScaling_noDrawable_defaultAspectRatio() {
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-        float aspectRatio = 2.0f;
-
-        mView.setDefaultAspectRatio(aspectRatio);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo((int) (CONTAINER_WIDTH / aspectRatio));
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_drawableBadDims_defaultAspectRatio() {
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-        float aspectRatio = 2.0f;
-
-        mDrawable = new ColorDrawable(Color.RED);
-
-        mView.setDefaultAspectRatio(aspectRatio);
-        mView.setImageDrawable(mDrawable);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo((int) (CONTAINER_WIDTH / aspectRatio));
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_bothExactly() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.EXACTLY);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_exactlyWidth_unspecifiedHeight() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_WIDTH / DRAWABLE_ASPECT_RATIO);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_exactlyHeight_unspecifiedWidth() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.EXACTLY);
-        int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_HEIGHT * DRAWABLE_ASPECT_RATIO);
-    }
-
-    @Test
-    public void testScaling_exactlyWidth_atMostHeight() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.AT_MOST);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.EXACTLY);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_exactlyHeight_atMostWidth() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.EXACTLY);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.AT_MOST);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_HEIGHT * DRAWABLE_ASPECT_RATIO);
-    }
-
-    @Test
-    public void testScaling_atMostHeightAndWidth_widerContainer() {
-        mDrawable = new BitmapDrawable(Bitmap.createBitmap(50, 100, Bitmap.Config.RGB_565));
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST);
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(200);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(100);
-    }
-
-    @Test
-    public void testScaling_atMostHeightAndWidth_tallerContainer() {
-        mDrawable = new BitmapDrawable(Bitmap.createBitmap(100, 50, Bitmap.Config.RGB_565));
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-        int widthSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(100);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(200);
-    }
-
-    @Test
-    public void testScaling_atMostWidth_unspecifiedHeight() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(CONTAINER_WIDTH, MeasureSpec.AT_MOST);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_WIDTH / DRAWABLE_ASPECT_RATIO);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_WIDTH);
-    }
-
-    @Test
-    public void testScaling_atMostHeight_unspecifiedWidth() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(CONTAINER_HEIGHT, MeasureSpec.AT_MOST);
-        int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(CONTAINER_HEIGHT);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(CONTAINER_HEIGHT * DRAWABLE_ASPECT_RATIO);
-    }
-
-    @Test
-    public void testScaling_bothUnspecified() {
-        mView.setImageDrawable(mDrawable);
-
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mView.measure(widthSpec, heightSpec);
-
-        assertThat(mView.getMeasuredHeight()).isEqualTo(0xFFFFFF);
-        assertThat(mView.getMeasuredWidth()).isEqualTo(0xFFFFFF);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BitmapMaskingRoundedCornerDelegateTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BitmapMaskingRoundedCornerDelegateTest.java
deleted file mode 100644
index 28a3099..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BitmapMaskingRoundedCornerDelegateTest.java
+++ /dev/null
@@ -1,152 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache.Corner;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link BitmapMaskingRoundedCornerDelegate}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class BitmapMaskingRoundedCornerDelegateTest {
-    @Mock
-    private Canvas mMockCanvas;
-    @Mock
-    private RoundedCornerMaskCache mCache;
-
-    private static final Supplier<Boolean> IS_RTL_SUPPLIER = Suppliers.of(false);
-    private RoundedCornerWrapperView mRoundedCornerWrapperView;
-    private Context mContext;
-    private static final int RADIUS = 10;
-
-    private Bitmap mTopLeft;
-    private Bitmap mTopRight;
-    private Bitmap mBottomLeft;
-    private Bitmap mBottomRight;
-    private RoundedCornerBitmaps mBitmaps;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mRoundedCornerWrapperView =
-                new RoundedCornerWrapperView(mContext, RoundedCorners.getDefaultInstance(), mCache,
-                        IS_RTL_SUPPLIER, 0, Borders.getDefaultInstance(), true, true);
-
-        RoundedCornerMaskCache realCache = new RoundedCornerMaskCache();
-        mBitmaps = realCache.getMasks(16);
-        mTopLeft = realCache.getMasks(16).get(Corner.TOP_LEFT);
-        mTopRight = realCache.getMasks(16).get(Corner.TOP_RIGHT);
-        mBottomLeft = realCache.getMasks(16).get(Corner.BOTTOM_LEFT);
-        mBottomRight = realCache.getMasks(16).get(Corner.BOTTOM_RIGHT);
-
-        when(mCache.getMasks(anyInt())).thenReturn(mBitmaps);
-        when(mCache.getMaskPaint()).thenReturn(new Paint());
-    }
-
-    @Test
-    public void maskCorners_radiusZero() {
-        BitmapMaskingRoundedCornerDelegate bitmapMaskingDelegate =
-                new BitmapMaskingRoundedCornerDelegate(
-                        mCache, /* bitmask= */ 15, /* isRtL= */ false, mMockCanvas);
-
-        mRoundedCornerWrapperView.layout(0, 0, 100, 100);
-
-        bitmapMaskingDelegate.onLayout(/* radius= */ 0, /* isRtL= */ false, 100, 100);
-        bitmapMaskingDelegate.draw(mRoundedCornerWrapperView, new Canvas());
-
-        verify(mMockCanvas, never())
-                .drawBitmap(any(Bitmap.class), anyFloat(), anyFloat(), any(Paint.class));
-    }
-
-    @Test
-    public void maskAndDrawCorners_allCorners() {
-        int all_corner_bitmask = 15;
-        boolean isRtL = false;
-        BitmapMaskingRoundedCornerDelegate bitmapMaskingDelegate =
-                new BitmapMaskingRoundedCornerDelegate(
-                        mCache, all_corner_bitmask, isRtL, mMockCanvas);
-
-        mRoundedCornerWrapperView.layout(0, 0, 100, 100);
-
-        bitmapMaskingDelegate.onLayout(RADIUS, isRtL, 100, 100);
-        bitmapMaskingDelegate.draw(mRoundedCornerWrapperView, new Canvas());
-
-        verify(mMockCanvas).drawBitmap(eq(mTopLeft), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mTopRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mBottomRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mBottomLeft), anyFloat(), anyFloat(), any(Paint.class));
-    }
-
-    @Test
-    public void maskCorners_topStart_bottomEnd() {
-        int topStart_bottomEnd_bitmask = 5;
-        boolean isRtL = false;
-
-        BitmapMaskingRoundedCornerDelegate bitmapMaskingDelegate =
-                new BitmapMaskingRoundedCornerDelegate(
-                        mCache, topStart_bottomEnd_bitmask, isRtL, mMockCanvas);
-
-        mRoundedCornerWrapperView.layout(0, 0, 100, 100);
-
-        bitmapMaskingDelegate.onLayout(RADIUS, isRtL, 100, 100);
-        bitmapMaskingDelegate.draw(mRoundedCornerWrapperView, new Canvas());
-
-        verify(mMockCanvas).drawBitmap(eq(mTopLeft), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas, never())
-                .drawBitmap(eq(mTopRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mBottomRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas, never())
-                .drawBitmap(eq(mBottomLeft), anyFloat(), anyFloat(), any(Paint.class));
-    }
-
-    @Test
-    public void maskCorners_topStart_bottomEnd_rtl() {
-        int topStart_bottomEnd_bitmask = 5;
-        boolean isRtL = true;
-        BitmapMaskingRoundedCornerDelegate bitmapMaskingDelegate =
-                new BitmapMaskingRoundedCornerDelegate(
-                        mCache, topStart_bottomEnd_bitmask, isRtL, mMockCanvas);
-
-        mRoundedCornerWrapperView.layout(0, 0, 100, 100);
-
-        bitmapMaskingDelegate.onLayout(RADIUS, isRtL, 100, 100);
-        bitmapMaskingDelegate.draw(mRoundedCornerWrapperView, new Canvas());
-
-        verify(mMockCanvas, never())
-                .drawBitmap(eq(mTopLeft), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mTopRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas, never())
-                .drawBitmap(eq(mBottomRight), anyFloat(), anyFloat(), any(Paint.class));
-        verify(mMockCanvas).drawBitmap(eq(mBottomLeft), anyFloat(), anyFloat(), any(Paint.class));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BorderDrawableTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BorderDrawableTest.java
deleted file mode 100644
index f411c8c..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BorderDrawableTest.java
+++ /dev/null
@@ -1,149 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.ui.LayoutUtils;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders.Edges;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link BorderDrawable}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class BorderDrawableTest {
-    private static final float[] ZERO_RADII = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
-    private static final boolean LEFT_TO_RIGHT = false;
-    private static final boolean RIGHT_TO_LEFT = true;
-
-    private static final int COLOR = 0xFFFF0000;
-    private static final int WIDTH_DP = 8;
-    private int mWidthPx;
-
-    private static final Borders DEFAULT_BORDER =
-            Borders.newBuilder().setWidth(WIDTH_DP).setColor(COLOR).build();
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mWidthPx = (int) LayoutUtils.dpToPx(WIDTH_DP, mContext);
-    }
-
-    @Test
-    public void testBorders_setsPaintParams() {
-        BorderDrawable borderDrawable =
-                new BorderDrawable(mContext, DEFAULT_BORDER, ZERO_RADII, LEFT_TO_RIGHT);
-
-        assertThat(borderDrawable.getPaint().getStyle()).isEqualTo(Paint.Style.STROKE);
-        assertThat(borderDrawable.getPaint().getStrokeWidth()).isEqualTo((float) mWidthPx * 2);
-        assertThat(borderDrawable.getPaint().getColor()).isEqualTo(COLOR);
-    }
-
-    @Test
-    public void testBorders_allSides_default() {
-        BorderDrawable borderDrawable =
-                new BorderDrawable(mContext, DEFAULT_BORDER, ZERO_RADII, LEFT_TO_RIGHT);
-
-        assertThat(borderDrawable.getPaint().getStyle()).isEqualTo(Paint.Style.STROKE);
-        assertThat(borderDrawable.getPaint().getStrokeWidth()).isEqualTo((float) mWidthPx * 2);
-        assertThat(borderDrawable.getPaint().getColor()).isEqualTo(COLOR);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1, 2, 3, 4));
-    }
-
-    @Test
-    public void testBorders_allSides_explicit() {
-        BorderDrawable borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.BOTTOM.getNumber() | Edges.TOP.getNumber()
-                                | Edges.START.getNumber() | Edges.END.getNumber())
-                        .build(),
-                ZERO_RADII, LEFT_TO_RIGHT);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1, 2, 3, 4));
-    }
-
-    @Test
-    public void testBorders_topLeft() {
-        BorderDrawable borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.TOP.getNumber() | Edges.START.getNumber())
-                        .build(),
-                ZERO_RADII, LEFT_TO_RIGHT);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1, 2, 3 + mWidthPx, 4 + mWidthPx));
-    }
-
-    @Test
-    public void testBorders_bottomRight() {
-        BorderDrawable borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.BOTTOM.getNumber() | Edges.END.getNumber())
-                        .build(),
-                ZERO_RADII, LEFT_TO_RIGHT);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1 - mWidthPx, 2 - mWidthPx, 3, 4));
-    }
-
-    @Test
-    public void testBorders_someSides_RtL() {
-        BorderDrawable borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.TOP.getNumber() | Edges.BOTTOM.getNumber()
-                                | Edges.END.getNumber())
-                        .build(),
-                ZERO_RADII, RIGHT_TO_LEFT);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1, 2, 3 + mWidthPx, 4));
-
-        borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.TOP.getNumber() | Edges.BOTTOM.getNumber()
-                                | Edges.START.getNumber())
-                        .build(),
-                ZERO_RADII, RIGHT_TO_LEFT);
-
-        borderDrawable.setBounds(1, 2, 3, 4);
-        bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1 - mWidthPx, 2, 3, 4));
-    }
-
-    @Test
-    public void testSetBoundsRect() {
-        BorderDrawable borderDrawable = new BorderDrawable(mContext,
-                DEFAULT_BORDER.toBuilder()
-                        .setBitmask(Edges.TOP.getNumber() | Edges.END.getNumber())
-                        .build(),
-                ZERO_RADII, LEFT_TO_RIGHT);
-
-        borderDrawable.setBounds(new Rect(1, 2, 3, 4));
-        Rect bounds = borderDrawable.getBounds();
-        assertThat(bounds).isEqualTo(new Rect(1 - mWidthPx, 2, 3, 4 + mWidthPx));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientDrawableTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientDrawableTest.java
deleted file mode 100644
index ba5df788..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientDrawableTest.java
+++ /dev/null
@@ -1,82 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.graphics.Color;
-import android.graphics.drawable.shapes.RectShape;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.ColorStop;
-import org.chromium.components.feed.core.proto.ui.piet.GradientsProto.LinearGradient;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link GradientDrawable}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class GradientDrawableTest {
-    @Test
-    public void testCreatesShader() {
-        GradientDrawable drawable = new GradientDrawable(
-                LinearGradient.newBuilder()
-                        .setDirectionDeg(123)
-                        .addStops(ColorStop.newBuilder().setPositionPercent(0).setColor(Color.BLUE))
-                        .addStops(
-                                ColorStop.newBuilder().setPositionPercent(100).setColor(Color.RED))
-                        .build(),
-                Suppliers.of(true));
-        assertThat(drawable.getShape()).isInstanceOf(RectShape.class);
-        assertThat(drawable.getShaderFactory()).isInstanceOf(GradientShader.class);
-    }
-
-    @Test
-    public void testCreatesShader_noRtlSupport() {
-        GradientDrawable drawable = new GradientDrawable(
-                LinearGradient.newBuilder()
-                        .setDirectionDeg(123)
-                        .addStops(ColorStop.newBuilder().setPositionPercent(0).setColor(Color.BLUE))
-                        .addStops(
-                                ColorStop.newBuilder().setPositionPercent(100).setColor(Color.RED))
-                        .build(),
-                Suppliers.of(true));
-
-        assertThat(((GradientShader) drawable.getShaderFactory()).mRtLSupplier).isNull();
-    }
-
-    @Test
-    public void testCreatesShader_rtl() {
-        GradientDrawable drawable = new GradientDrawable(
-                LinearGradient.newBuilder()
-                        .setDirectionDeg(123)
-                        .addStops(ColorStop.newBuilder().setPositionPercent(0).setColor(Color.BLUE))
-                        .addStops(
-                                ColorStop.newBuilder().setPositionPercent(100).setColor(Color.RED))
-                        .setReverseForRtl(true)
-                        .build(),
-                Suppliers.of(true));
-
-        assertThat(((GradientShader) drawable.getShaderFactory()).mRtLSupplier.get()).isTrue();
-    }
-
-    @Test
-    public void testCreatesShader_ltr() {
-        GradientDrawable drawable = new GradientDrawable(
-                LinearGradient.newBuilder()
-                        .setDirectionDeg(123)
-                        .addStops(ColorStop.newBuilder().setPositionPercent(0).setColor(Color.BLUE))
-                        .addStops(
-                                ColorStop.newBuilder().setPositionPercent(100).setColor(Color.RED))
-                        .setReverseForRtl(true)
-                        .build(),
-                Suppliers.of(false));
-
-        assertThat(((GradientShader) drawable.getShaderFactory()).mRtLSupplier.get()).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientShaderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientShaderTest.java
deleted file mode 100644
index 767af3ff..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientShaderTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.graphics.Color;
-import android.graphics.RectF;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests for the {@link GradientShader}.
- *
- * <p>Note that on Android, the Y axis starts at the top and increases to the bottom, so some of the
- * math is backward.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class GradientShaderTest {
-    private static final boolean RTL = true;
-    private static final boolean LTR = false;
-
-    @Test
-    public void testGradientLine_leftToRight() {
-        RectF points = GradientShader.calculateGradientLine(12, 12, Math.toRadians(90), 0, LTR);
-        checkGradientLine(points, 0, 6, 12, 6);
-    }
-
-    @Test
-    public void testGradientLine_bottomToTop() {
-        RectF points = GradientShader.calculateGradientLine(12, 12, 0, 0, LTR);
-        checkGradientLine(points, 6, 12, 6, 0);
-    }
-
-    @Test
-    public void testGradientLine_bottomLeftToTopRight() {
-        RectF points = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(45), Math.toRadians(45), LTR);
-        checkGradientLine(points, 0, 12, 12, 0);
-    }
-
-    @Test
-    public void testGradientLine_bottomRightToTopLeft() {
-        RectF points = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(315), Math.toRadians(315 % 90), LTR);
-        checkGradientLine(points, 12, 12, 0, 0);
-    }
-
-    @Test
-    public void testGradientLine_thirtyDegrees() {
-        RectF points = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(30), Math.toRadians(30), LTR);
-        checkGradientLine(points, 1.9019f, 13.0981f, 10.0981f, -1.0981f);
-    }
-
-    @Test
-    public void testGradientLine_threeThirtyDegrees() {
-        RectF points = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(330), Math.toRadians(330 % 90), LTR);
-        checkGradientLine(points, 10.0981f, 13.0981f, 1.9019f, -1.0981f);
-    }
-
-    @Test
-    public void testGradientLine_horizontalRtL() {
-        RectF points = GradientShader.calculateGradientLine(12, 12, Math.toRadians(90), 0, RTL);
-        checkGradientLine(points, 12, 6, 0, 6);
-
-        // Flip the angle 180; should be the same as the RtL version
-        RectF pointsLtR = GradientShader.calculateGradientLine(12, 12, Math.toRadians(270), 0, LTR);
-        checkGradientLine(points, pointsLtR.left, pointsLtR.top, pointsLtR.right, pointsLtR.bottom);
-    }
-
-    @Test
-    public void testGradientLine_verticalRtL() {
-        RectF points = GradientShader.calculateGradientLine(12, 12, 0, 0, LTR);
-        checkGradientLine(points, 6, 12, 6, 0);
-
-        // RtL should not change the points for a vertical line
-        RectF pointsLtR = GradientShader.calculateGradientLine(12, 12, 0, 0, LTR);
-        checkGradientLine(points, pointsLtR.left, pointsLtR.top, pointsLtR.right, pointsLtR.bottom);
-    }
-
-    @Test
-    public void testGradientLine_angleRtL() {
-        RectF points = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(45), Math.toRadians(45), RTL);
-        checkGradientLine(points, 12, 12, 0, 0);
-
-        RectF pointsLtR = GradientShader.calculateGradientLine(
-                12, 12, Math.toRadians(360 - 45), Math.toRadians(45), LTR);
-        checkGradientLine(points, pointsLtR.left, pointsLtR.top, pointsLtR.right, pointsLtR.bottom);
-    }
-
-    @Test
-    public void testGradientLine_invalidDegrees() {
-        GradientShader shader = new GradientShader(
-                new int[] {Color.BLUE, Color.RED}, new float[] {0, 1}, 999, Suppliers.of(LTR));
-        assertThat(shader.mAngleRadians).isEqualTo(Math.toRadians(999 % 360));
-        assertThat(shader.mAngleRadiansSmall).isEqualTo(Math.toRadians(999 % 90));
-    }
-
-    @Test
-    public void testGradientLine_invalidStops() {
-        GradientShader shader = new GradientShader(
-                new int[] {Color.BLUE, Color.RED}, new float[] {999, -1}, 45, Suppliers.of(LTR));
-        shader.resize(123, 456);
-
-        // assert no failures
-    }
-
-    @Test
-    public void testGradientLine_lengthMismatch() {
-        assertThatRunnable(()
-                                   -> new GradientShader(new int[] {Color.BLUE, Color.RED},
-                                           new float[] {1}, 45, Suppliers.of(LTR)))
-                .throwsAnExceptionOfType(IllegalStateException.class)
-                .that()
-                .hasMessageThat()
-                .contains("Mismatch: got 2 colors and 1 stops");
-    }
-
-    @Test
-    public void testGradientLine_nullLtRSupplier() {
-        GradientShader shader =
-                new GradientShader(new int[] {Color.BLUE, Color.RED}, new float[] {0, 1}, 45, null);
-        shader.resize(123, 456);
-
-        // assert no failures
-    }
-
-    private void checkGradientLine(
-            RectF result, float startx, float starty, float endx, float endy) {
-        String testFailOutput = String.format(
-                "expected: %s\nbut was : %s", new RectF(startx, starty, endx, endy), result);
-        assertWithMessage(testFailOutput).that(result.left).isWithin(0.01f).of(startx);
-        assertWithMessage(testFailOutput).that(result.top).isWithin(0.01f).of(starty);
-        assertWithMessage(testFailOutput).that(result.right).isWithin(0.01f).of(endx);
-        assertWithMessage(testFailOutput).that(result.bottom).isWithin(0.01f).of(endy);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GridRowViewTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GridRowViewTest.java
deleted file mode 100644
index ea63e1d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GridRowViewTest.java
+++ /dev/null
@@ -1,1011 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.piet.ui.GridRowView.LayoutParams;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link GridRowView}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class GridRowViewTest {
-    private static final int WIDTH = 100;
-    private static final int WRAP_CONTENT_WIDTH = 75;
-    private static final int COLLAPSIBLE_WIDTH = 80;
-    private static final int HEIGHT = 1234;
-    private static final int LARGE_HEIGHT = 10000;
-
-    private int mUnspecifiedSpec;
-
-    private Context mContext;
-
-    private TestView mCollapsibleView;
-    private TestView mCollapsibleView2;
-    private TestView mWrapContentView;
-    private TestView mWidthView;
-    private TestView mWeightView;
-    private TestView mWeightView2;
-    private TestView mGoneView;
-
-    private GridRowView mGridRowView;
-
-    @Before
-    public void setUp() {
-        mContext = Robolectric.buildActivity(Activity.class).get();
-
-        mUnspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mGridRowView = new GridRowView(mContext, Suppliers.of(false));
-
-        mCollapsibleView = new TestView(mContext, COLLAPSIBLE_WIDTH, HEIGHT);
-        mCollapsibleView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, HEIGHT, 0.0f, true));
-
-        mCollapsibleView2 = new TestView(mContext, COLLAPSIBLE_WIDTH, HEIGHT);
-        mCollapsibleView2.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, HEIGHT, 0.0f, true));
-
-        mWrapContentView = new TestView(mContext, WRAP_CONTENT_WIDTH, HEIGHT);
-        mWrapContentView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, HEIGHT, 0.0f, false));
-        // Not sure how to get the wrap content width
-
-        mWidthView = new TestView(mContext, WIDTH, HEIGHT);
-        mWidthView.setLayoutParams(new LayoutParams(WIDTH, HEIGHT, 0.0f, false));
-
-        mWeightView = new TestView(mContext, 0, HEIGHT);
-        mWeightView.setLayoutParams(new LayoutParams(0, HEIGHT, 1.0f, false));
-
-        mWeightView2 = new TestView(mContext, 0, HEIGHT);
-        mWeightView2.setLayoutParams(new LayoutParams(0, HEIGHT, 1.0f, false));
-
-        mGoneView = new TestView(mContext, WIDTH, LARGE_HEIGHT);
-        mGoneView.setMinimumHeight(LARGE_HEIGHT);
-        mGoneView.setMinimumWidth(WIDTH);
-        mGoneView.setVisibility(View.GONE);
-    }
-
-    @Test
-    public void testOnlyHorizontal() {
-        assertThat(mGridRowView.getOrientation()).isEqualTo(LinearLayout.HORIZONTAL);
-        assertThatRunnable(() -> mGridRowView.setOrientation(LinearLayout.VERTICAL))
-                .throwsAnExceptionOfType(IllegalArgumentException.class)
-                .that()
-                .hasMessageThat()
-                .contains("GridRowView can only be horizontal");
-    }
-
-    @Test
-    public void testCollapsibleIgnoresWeight() {
-        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, HEIGHT, 0.0f, true);
-        assertThat(params.getIsCollapsible()).isTrue();
-
-        params = new LayoutParams(123, HEIGHT, 0.0f, true);
-        assertThat(params.getIsCollapsible()).isTrue();
-
-        params = new LayoutParams(0, HEIGHT, 1.0f, true);
-        assertThat(params.getIsCollapsible()).isFalse();
-    }
-
-    @Test
-    public void testGenerateDefaultLayoutParams() {
-        LayoutParams layoutParams = mGridRowView.generateDefaultLayoutParams();
-        assertThat(layoutParams.width).isEqualTo(0);
-        assertThat(layoutParams.height).isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(layoutParams.weight).isEqualTo(1.0f);
-        assertThat(layoutParams.getIsCollapsible()).isFalse();
-    }
-
-    @Test
-    public void testGenerateLayoutParams() {
-        LayoutParams result;
-
-        // not LinearLayout
-        ViewGroup.LayoutParams viewGroupLayoutParams = new ViewGroup.LayoutParams(12, 34);
-        result = mGridRowView.generateLayoutParams(viewGroupLayoutParams);
-        assertThat(result.width).isEqualTo(12);
-        assertThat(result.height).isEqualTo(34);
-
-        // LinearLayout
-        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(12, 34, 56);
-        result = mGridRowView.generateLayoutParams(linearLayoutParams);
-        assertThat(result.width).isEqualTo(12);
-        assertThat(result.height).isEqualTo(34);
-        assertThat(result.weight).isEqualTo(56.0f);
-
-        // GridRowView
-        GridRowView.LayoutParams gridRowParams =
-                new LayoutParams(LayoutParams.WRAP_CONTENT, 34, 0, true);
-        result = mGridRowView.generateLayoutParams(gridRowParams);
-        assertThat(result.width).isEqualTo(LayoutParams.WRAP_CONTENT);
-        assertThat(result.height).isEqualTo(34);
-        assertThat(result.weight).isEqualTo(0.0f);
-        assertThat(result.getIsCollapsible()).isTrue();
-    }
-
-    @Test
-    public void testCheckLayoutParams() {
-        // not LinearLayout
-        ViewGroup.LayoutParams viewGroupLayoutParams = new ViewGroup.LayoutParams(12, 34);
-        assertThat(mGridRowView.checkLayoutParams(viewGroupLayoutParams)).isFalse();
-
-        // LinearLayout
-        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(12, 34, 56);
-        assertThat(mGridRowView.checkLayoutParams(linearLayoutParams)).isFalse();
-
-        // GridRowView
-        GridRowView.LayoutParams gridRowParams =
-                new LayoutParams(LayoutParams.WRAP_CONTENT, 34, 0, true);
-        assertThat(mGridRowView.checkLayoutParams(gridRowParams)).isTrue();
-    }
-
-    @Test
-    public void testOnMeasure_noCollapsible() {
-        // No special processing
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mWeightView);
-        mGridRowView.addView(mWeightView2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(100); // WIDTH
-        assertThat(mWeightView.mRequestedWidth).isEqualTo(150); // (400 - WIDTH) / 2
-        assertThat(mWeightView2.mRequestedWidth).isEqualTo(150);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(400);
-    }
-
-    @Test
-    public void testOnMeasure_unspecifiedWidth() {
-        // No special processing; collapsible cell is WRAP_CONTENT
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.measure(mUnspecifiedSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(100); // WIDTH
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(255); // 100 + 80 + 75
-    }
-
-    @Test
-    public void testOnMeasure_moreSpaceThanNecessary() {
-        // DP, Collapsible, WRAP_CONTENT
-        // Extra space is left over
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(400);
-    }
-
-    @Test
-    public void testOnMeasure_notEnoughSpace() {
-        // DP, Collapsible, WRAP_CONTENT
-        // Collapsible cell is truncated
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(200 - WIDTH - WRAP_CONTENT_WIDTH);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(200);
-    }
-
-    @Test
-    public void testOnMeasure_noSpace() {
-        // DP, Collapsible, WRAP_CONTENT
-        // Collapsible cell is completely gone
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(160, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(0);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(160 - WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(160);
-    }
-
-    @Test
-    public void testOnMeasure_twoCollapsible_spaceForBoth() {
-        // Both cells WRAP_CONTENT and there is space left over
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mCollapsibleView2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mCollapsibleView2.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(200);
-    }
-
-    @Test
-    public void testOnMeasure_twoCollapsible_spaceForFirst() {
-        // First cell should WRAP_CONTENT, second cell shrinks
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mCollapsibleView2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mCollapsibleView2.mRequestedWidth).isEqualTo(100 - COLLAPSIBLE_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_twoCollapsible_spaceForNeither() {
-        // First cell fills row, and second gets no space
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mCollapsibleView2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(50);
-        assertThat(mCollapsibleView2.mRequestedWidth).isEqualTo(0);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(50);
-    }
-
-    @Test
-    public void testOnMeasure_collapsibleWithWeight_enoughSpace() {
-        // Collapsible cell should WRAP_CONTENT, and weight fills the rest
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWeightView);
-        mGridRowView.addView(mWeightView2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mWeightView.mRequestedWidth).isEqualTo((100 - COLLAPSIBLE_WIDTH) / 2);
-        assertThat(mWeightView2.mRequestedWidth).isEqualTo((100 - COLLAPSIBLE_WIDTH) / 2);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_collapsibleWithWeight_notEnoughSpace() {
-        // Collapsible cell should take up the entire row
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWeightView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(50);
-        assertThat(mWeightView.mRequestedWidth).isEqualTo(0);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(50);
-    }
-
-    @Test
-    public void testOnMeasure_widthCollapsible_enoughSpace() {
-        // DP, Collapsible, WRAP
-        // Extra space is left over
-
-        mCollapsibleView.setLayoutParams(new LayoutParams(COLLAPSIBLE_WIDTH, HEIGHT, 0.0f, true));
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(400);
-    }
-
-    @Test
-    public void testOnMeasure_widthCollapsible_notEnoughSpace() {
-        // DP, Collapsible, WRAP
-        // Collapsible cell is truncated
-
-        mCollapsibleView.setLayoutParams(new LayoutParams(COLLAPSIBLE_WIDTH, HEIGHT, 0.0f, true));
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(200 - WIDTH - WRAP_CONTENT_WIDTH);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(200);
-    }
-
-    @Test
-    public void testOnMeasure_cellThatGetsTallerWithWidth() {
-        // Make a cell where width = height and weight = 1 and ensure it behaves with a collapsible
-
-        mCollapsibleView = new TestView(mContext, COLLAPSIBLE_WIDTH, 20);
-        mCollapsibleView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, 20, 0.0f, true));
-
-        SquareView squareView = new SquareView(mContext);
-        squareView.setLayoutParams(new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f, false));
-
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(squareView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(130, MeasureSpec.EXACTLY);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        int squareSize = 130 - COLLAPSIBLE_WIDTH;
-        assertThat(mCollapsibleView.mRequestedWidth).isEqualTo(COLLAPSIBLE_WIDTH);
-        assertThat(squareView.getMeasuredWidth()).isEqualTo(squareSize);
-        assertThat(squareView.getMeasuredHeight()).isEqualTo(squareSize);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(130);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(squareSize);
-    }
-
-    @Test
-    public void testOnMeasure_wrapContentMatchesCellHeight() {
-        // Several different sizes of cells; ensure that height of GridRowView is same as tallest
-        // cell.
-
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(new LayoutParams(20, 20, 0.0f, true));
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(new LayoutParams(40, 40, 0.0f, true));
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(new LayoutParams(30, 30, 0.0f, true));
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(40);
-    }
-
-    @Test
-    public void testOnMeasure_doesNotWrapHeightWhenMeasureExactly() {
-        // Several different sizes of cells; ensure that height of GridRowView is same as tallest
-        // cell.
-
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(new LayoutParams(20, 20, 0.0f, true));
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(new LayoutParams(40, 40, 0.0f, true));
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(new LayoutParams(30, 30, 0.0f, true));
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, 100));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_shrinksCellThatWantsToBeTooTall() {
-        // Several different sizes of cells; ensure that height of GridRowView is same as tallest
-        // cell.
-
-        TestView view = new TestView(mContext, 150, 200);
-        view.setLayoutParams(new LayoutParams(150, 200, 0.0f, true));
-
-        mGridRowView.addView(view);
-
-        mGridRowView.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, 100));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(100);
-        assertThat(view.mRequestedWidth).isEqualTo(100);
-        assertThat(view.mRequestedHeight).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_respectsMinHeight() {
-        // Several different sizes of cells; ensure that height of GridRowView is the min height.
-
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(new LayoutParams(20, 20, 0.0f, true));
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(new LayoutParams(40, 40, 0.0f, true));
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(new LayoutParams(30, 30, 0.0f, true));
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        mGridRowView.setMinimumHeight(95);
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(95);
-    }
-
-    @Test
-    public void testOnMeasure_respectsMinHeightUpToAtMost() {
-        // Several different sizes of cells; ensure that height of GridRowView is the min height.
-
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(new LayoutParams(20, 20, 0.0f, true));
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(new LayoutParams(40, 40, 0.0f, true));
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(new LayoutParams(30, 30, 0.0f, true));
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        mGridRowView.setMinimumHeight(1234);
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_respectsChildLayoutParams_weight() {
-        // Several different sizes of cells; ensure that width of each cell is set by the style
-
-        int styleWidth = 30;
-        LayoutParams cellLayoutParams =
-                new LayoutParams(styleWidth, LayoutParams.WRAP_CONTENT, 0.0f, true);
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(cellLayoutParams);
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(cellLayoutParams);
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(cellLayoutParams);
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(square1.mRequestedWidth).isEqualTo(styleWidth);
-        assertThat(square2.mRequestedWidth).isEqualTo(styleWidth);
-        assertThat(square3.mRequestedWidth).isEqualTo(styleWidth);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(200);
-    }
-
-    @Test
-    public void testOnMeasure_respectsChildLayoutParams_height() {
-        // Several different sizes of cells; ensure that height of GridRowView is same as tallest
-        // cell.
-
-        int styleHeight = 30;
-        LayoutParams cellLayoutParams =
-                new LayoutParams(LayoutParams.WRAP_CONTENT, styleHeight, 0.0f, true);
-        TestView square1 = new TestView(mContext, 20, 20);
-        square1.setLayoutParams(cellLayoutParams);
-        TestView square2 = new TestView(mContext, 40, 40);
-        square2.setLayoutParams(cellLayoutParams);
-        TestView square3 = new TestView(mContext, 30, 30);
-        square3.setLayoutParams(cellLayoutParams);
-
-        mGridRowView.addView(square1);
-        mGridRowView.addView(square2);
-        mGridRowView.addView(square3);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(square1.mRequestedHeight).isEqualTo(styleHeight);
-        assertThat(square2.mRequestedHeight).isEqualTo(styleHeight);
-        assertThat(square3.mRequestedHeight).isEqualTo(styleHeight);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(styleHeight);
-    }
-
-    @Test
-    public void testOnMeasure_rowHeightIsMaxCellHeightWithMargins() {
-        // Several different sizes of cells; ensure that height of GridRowView is same as tallest
-        // cell.
-
-        TestView view1 = new TestView(mContext, 40, 40);
-        view1.setLayoutParams(new LayoutParams(40, 40));
-
-        TestView view2 = new TestView(mContext, 30, 30);
-        LayoutParams params = new LayoutParams(30, 30);
-        params.topMargin = 5;
-        params.bottomMargin = 15;
-        view2.setLayoutParams(params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(100);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(50);
-    }
-
-    @Test
-    public void testOnMeasure_cellWithMarginsClippedByHeightConstraint() {
-        // Row is 100 high, cell is set to 90 high, but has 5 + 15 margins, so cell is only 80 high.
-
-        TestView view = new TestView(mContext, 123, 90);
-        LayoutParams params = new LayoutParams(123, 90);
-        params.topMargin = 5;
-        params.bottomMargin = 15;
-        view.setLayoutParams(params);
-
-        mGridRowView.addView(view);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-
-        mGridRowView.measure(widthSpec, heightSpec);
-
-        assertThat(view.mRequestedHeight).isEqualTo(80);
-    }
-
-    @Test
-    public void testOnMeasure_respectsGridRowPadding() {
-        // DP, Collapsible, WRAP_CONTENT
-        // Collapsible cell is truncated
-
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mCollapsibleView);
-        mGridRowView.addView(mWrapContentView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        int paddingLeft = 10;
-        int paddingTop = 20;
-        int paddingRight = 30;
-        int paddingBottom = 40;
-        mGridRowView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
-
-        int sizeSpec = MeasureSpec.makeMeasureSpec(250, MeasureSpec.EXACTLY);
-        mGridRowView.measure(sizeSpec, sizeSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mCollapsibleView.mRequestedWidth)
-                .isEqualTo(250 - WIDTH - WRAP_CONTENT_WIDTH - paddingLeft - paddingRight);
-        assertThat(mWrapContentView.mRequestedWidth).isEqualTo(WRAP_CONTENT_WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(250);
-
-        int cellHeight = 250 - paddingTop - paddingBottom;
-        assertThat(mWidthView.mRequestedHeight).isEqualTo(cellHeight);
-        assertThat(mCollapsibleView.mRequestedHeight).isEqualTo(cellHeight);
-        assertThat(mWrapContentView.mRequestedHeight).isEqualTo(cellHeight);
-        assertThat(mGridRowView.getMeasuredHeight()).isEqualTo(250);
-    }
-
-    @Test
-    public void testOnMeasure_fillParentCellsWithFitContentRow() {
-        TestView tallView = new TestView(mContext, 11, 100);
-        tallView.setLayoutParams(new LayoutParams(11, ViewGroup.LayoutParams.MATCH_PARENT));
-        TestView shortView = new TestView(mContext, 12, 20);
-        shortView.setLayoutParams(new LayoutParams(12, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        mGridRowView.addView(tallView);
-        mGridRowView.addView(shortView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(new LayoutParams(50, ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mGridRowView.measure(widthSpec, heightSpec);
-
-        assertThat(tallView.mRequestedHeight).isEqualTo(100);
-        assertThat(shortView.mRequestedHeight).isEqualTo(100);
-    }
-
-    @Test
-    public void testOnMeasure_fitContentCellsWithFitContentRow_minHeight() {
-        TestView tallView = new TestView(mContext, 11, 50);
-        tallView.setLayoutParams(new LayoutParams(11, ViewGroup.LayoutParams.WRAP_CONTENT));
-        TestView shortView = new TestView(mContext, 12, 20);
-        shortView.setLayoutParams(new LayoutParams(12, ViewGroup.LayoutParams.WRAP_CONTENT));
-        shortView.setMinimumHeight(30);
-
-        mGridRowView.addView(tallView);
-        mGridRowView.addView(shortView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(new LayoutParams(50, ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        mGridRowView.measure(widthSpec, heightSpec);
-
-        assertThat(tallView.mRequestedHeight).isEqualTo(50);
-        assertThat(shortView.mRequestedHeight).isEqualTo(30);
-    }
-
-    @Test
-    public void testOnMeasure_fitContentWidthRowIsTotalOfCells() {
-        TestView view1 = new TestView(mContext, 11, 100);
-        view1.setLayoutParams(new LayoutParams(11, 100));
-        TestView view2 = new TestView(mContext, 22, 100);
-        view2.setLayoutParams(new LayoutParams(22, 100));
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(view1.mRequestedWidth).isEqualTo(11);
-        assertThat(view2.mRequestedWidth).isEqualTo(22);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(33);
-    }
-
-    @Test
-    public void testOnMeasure_fitContentWidthRowWithWeightCellsTakesAllSpace() {
-        mGridRowView.addView(mWidthView);
-        mGridRowView.addView(mWeightView);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mWidthView.mRequestedWidth).isEqualTo(WIDTH);
-        assertThat(mWeightView.mRequestedWidth).isEqualTo(400 - WIDTH);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(400);
-    }
-
-    @Test
-    public void testOnMeasure_fitContentWidthRowWithMargins() {
-        TestView view1 = new TestView(mContext, 11, 100);
-        LayoutParams view1Params = new LayoutParams(11, 100);
-        view1Params.leftMargin = 2;
-        view1Params.rightMargin = 3;
-        view1.setLayoutParams(view1Params);
-
-        TestView view2 = new TestView(mContext, 22, 100);
-        LayoutParams view2Params = new LayoutParams(22, 100);
-        view2Params.leftMargin = 4;
-        view2Params.rightMargin = 5;
-        view2.setLayoutParams(view2Params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-        // Verify views which are gone are not considered in the layout.
-        mGridRowView.addView(mGoneView);
-
-        mGridRowView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(view1.mRequestedWidth).isEqualTo(11);
-        assertThat(view2.mRequestedWidth).isEqualTo(22);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(33 + 14);
-    }
-
-    @Test
-    public void testOnMeasure_fitContentWidthRowIsTotalOfCells_collapsibleAndMargins() {
-        TestView view1 = new TestView(mContext, 11, 100);
-        LayoutParams view1Params = new LayoutParams(11, 100);
-        view1Params.leftMargin = 2;
-        view1Params.rightMargin = 3;
-        view1.setLayoutParams(view1Params);
-
-        TestView view2 = new TestView(mContext, 22, 100);
-        LayoutParams view2Params = new LayoutParams(22, 100);
-        view2Params.leftMargin = 4;
-        view2Params.rightMargin = 5;
-        view2.setLayoutParams(view2Params);
-
-        TestView view3 = new TestView(mContext, 33, 100);
-        LayoutParams view3Params = new LayoutParams(LayoutParams.WRAP_CONTENT, 100, 0.0f, true);
-        view3Params.leftMargin = 6;
-        view3Params.rightMargin = 7;
-        view3.setLayoutParams(view3Params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-        mGridRowView.addView(view3);
-        mGridRowView.setLayoutParams(
-                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(view1.mRequestedWidth).isEqualTo(11);
-        assertThat(view2.mRequestedWidth).isEqualTo(22);
-        assertThat(view3.mRequestedWidth).isEqualTo(33);
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(11 + 22 + 33 + 2 + 3 + 4 + 5 + 6 + 7);
-    }
-
-    @Test
-    public void testOnMeasure_truncatesCellsWithMarginsAndPadding() {
-        // Cells will fit in the space, but margins push the second cell outside the row
-
-        TestView view1 = new TestView(mContext, 11, 100);
-        LayoutParams view1Params = new LayoutParams(11, 100);
-        view1Params.leftMargin = 2;
-        view1Params.rightMargin = 3;
-        view1.setLayoutParams(view1Params);
-
-        TestView view2 = new TestView(mContext, 22, 100);
-        LayoutParams view2Params = new LayoutParams(22, 100);
-        view2Params.leftMargin = 4;
-        view2Params.rightMargin = 5;
-        view2.setLayoutParams(view2Params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-
-        mGridRowView.setPadding(1, 0, 1, 0);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(33, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(view1.mRequestedWidth).isEqualTo(11);
-        assertThat(view2.mRequestedWidth).isEqualTo(33 - 11 - 1 - 1 - 2 - 3 - 4); // 11
-        assertThat(mGridRowView.getMeasuredWidth()).isEqualTo(33);
-    }
-
-    @Test
-    public void testCalculatesTotalWidth_noWeightCells() {
-        TestView view1 = new TestView(mContext, 11, 100);
-        LayoutParams view1Params = new LayoutParams(11, 100);
-        view1Params.leftMargin = 2;
-        view1Params.rightMargin = 3;
-        view1.setLayoutParams(view1Params);
-
-        TestView view2 = new TestView(mContext, 22, 100);
-        LayoutParams view2Params = new LayoutParams(22, 100);
-        view2Params.leftMargin = 4;
-        view2Params.rightMargin = 5;
-        view2.setLayoutParams(view2Params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-
-        mGridRowView.setPadding(6, 0, 7, 0);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mGridRowView.mTotalContentWidth).isEqualTo(11 + 2 + 3 + 22 + 4 + 5 + 6 + 7);
-    }
-
-    @Test
-    public void testCalculatesTotalWidth_weightCells() {
-        TestView view1 = new TestView(mContext, 11, 100);
-        LayoutParams view1Params = new LayoutParams(11, 100);
-        view1Params.leftMargin = 2;
-        view1Params.rightMargin = 3;
-        view1.setLayoutParams(view1Params);
-
-        TestView view2 = new TestView(mContext, 22, 100);
-        LayoutParams view2Params = new LayoutParams(0, 100, 1);
-        view2Params.leftMargin = 4;
-        view2Params.rightMargin = 5;
-        view2.setLayoutParams(view2Params);
-
-        mGridRowView.addView(view1);
-        mGridRowView.addView(view2);
-
-        mGridRowView.setPadding(6, 0, 7, 0);
-
-        int widthSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
-        mGridRowView.measure(widthSpec, mUnspecifiedSpec);
-
-        assertThat(mGridRowView.mTotalContentWidth).isEqualTo(100);
-    }
-
-    private static class TestView extends View {
-        private final int mDesiredWidth;
-        private final int mDesiredHeight;
-
-        int mRequestedWidth = -3;
-        int mRequestedHeight = -3;
-
-        TestView(Context context, int desiredWidth, int desiredHeight) {
-            super(context);
-            this.mDesiredWidth = desiredWidth;
-            this.mDesiredHeight = desiredHeight;
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
-                mRequestedWidth = MeasureSpec.getSize(widthMeasureSpec);
-            } else {
-                mRequestedWidth = mDesiredWidth;
-            }
-            if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) {
-                mRequestedHeight = MeasureSpec.getSize(heightMeasureSpec);
-            } else {
-                mRequestedHeight = mDesiredHeight;
-            }
-
-            int measuredWidth;
-            int measuredHeight;
-            switch (MeasureSpec.getMode(widthMeasureSpec)) {
-                case MeasureSpec.UNSPECIFIED:
-                    measuredWidth = mDesiredWidth;
-                    break;
-                case MeasureSpec.EXACTLY:
-                    measuredWidth = mRequestedWidth;
-                    break;
-                case MeasureSpec.AT_MOST:
-                    measuredWidth = Math.min(mRequestedWidth, mDesiredWidth);
-                    break;
-                default:
-                    measuredWidth = 999999;
-            }
-            switch (MeasureSpec.getMode(heightMeasureSpec)) {
-                case MeasureSpec.UNSPECIFIED:
-                    measuredHeight = mDesiredHeight;
-                    break;
-                case MeasureSpec.EXACTLY:
-                    measuredHeight = mRequestedHeight;
-                    break;
-                case MeasureSpec.AT_MOST:
-                    measuredHeight = Math.min(mRequestedHeight, mDesiredHeight);
-                    break;
-                default:
-                    measuredHeight = 999999;
-            }
-
-            setMeasuredDimension(measuredWidth, measuredHeight);
-        }
-    }
-
-    private static class SquareView extends View {
-        SquareView(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            int width;
-            if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
-                width = 999999;
-            } else {
-                width = MeasureSpec.getSize(widthMeasureSpec);
-            }
-            setMeasuredDimension(width, width);
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerMaskCacheTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerMaskCacheTest.java
deleted file mode 100644
index 61af8b4..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerMaskCacheTest.java
+++ /dev/null
@@ -1,103 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import android.graphics.Bitmap;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache.Corner;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link RoundedCornerMaskCache}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RoundedCornerMaskCacheTest {
-    private RoundedCornerMaskCache mCache;
-
-    @Before
-    public void setUp() {
-        mCache = new RoundedCornerMaskCache();
-    }
-
-    @Test
-    public void testGetBitmaps_createsNewInstance() {
-        RoundedCornerBitmaps masks = mCache.getMasks(16);
-
-        for (int i = 0; i < 4; i++) {
-            Bitmap mask = masks.get(i);
-            assertThat(mask.getWidth()).isEqualTo(16);
-            assertThat(mask.getHeight()).isEqualTo(16);
-        }
-    }
-
-    @Test
-    public void testGetBitmaps_differentRadii() {
-        RoundedCornerBitmaps masksFive = mCache.getMasks(5);
-        RoundedCornerBitmaps masksTen = mCache.getMasks(10);
-
-        assertThat(masksFive).isNotEqualTo(masksTen);
-
-        Bitmap maskFive = masksFive.get(Corner.TOP_LEFT);
-        Bitmap maskTen = masksTen.get(Corner.TOP_LEFT);
-
-        assertThat(maskFive).isNotEqualTo(maskTen);
-        assertThat(maskFive.getWidth()).isEqualTo(5);
-        assertThat(maskTen.getWidth()).isEqualTo(10);
-    }
-
-    @Test
-    public void testGetBitmaps_cachesInstance() {
-        RoundedCornerBitmaps masks1 = mCache.getMasks(16);
-
-        RoundedCornerBitmaps masks2 = mCache.getMasks(16);
-
-        assertThat(masks1).isSameInstanceAs(masks2);
-    }
-
-    @Test
-    public void testPurge() {
-        RoundedCornerBitmaps masks1 = mCache.getMasks(16);
-
-        mCache.purge();
-
-        RoundedCornerBitmaps masks2 = mCache.getMasks(16);
-
-        assertThat(masks1).isNotSameInstanceAs(masks2);
-    }
-
-    @Test
-    public void testBadCornerException() {
-        RoundedCornerBitmaps masks = mCache.getMasks(16);
-
-        assertThatRunnable(() -> masks.get(999))
-                .throwsAnExceptionOfType(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void testCreatesOnlyRequestedCorners() {
-        RoundedCornerBitmaps masks = mCache.getMasks(123);
-
-        assertThat(masks.mMasks[Corner.TOP_LEFT]).isNull();
-        assertThat(masks.mMasks[Corner.TOP_RIGHT]).isNull();
-        assertThat(masks.mMasks[Corner.BOTTOM_LEFT]).isNull();
-        assertThat(masks.mMasks[Corner.BOTTOM_RIGHT]).isNull();
-
-        masks.get(Corner.TOP_RIGHT);
-
-        assertThat(masks.mMasks[Corner.TOP_LEFT]).isNull();
-        assertThat(masks.mMasks[Corner.TOP_RIGHT]).isNotNull();
-        assertThat(masks.mMasks[Corner.BOTTOM_LEFT]).isNull();
-        assertThat(masks.mMasks[Corner.BOTTOM_RIGHT]).isNull();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerViewHelperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerViewHelperTest.java
deleted file mode 100644
index 1913ff0..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerViewHelperTest.java
+++ /dev/null
@@ -1,81 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link RoundedCornerViewHelper}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RoundedCornerViewHelperTest {
-    @Test
-    public void hasNoValidRoundedCorners_noRadiusSet() {
-        RoundedCorners roundedCorners = RoundedCorners.getDefaultInstance();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 0);
-
-        assertThat(calculatedValidity).isFalse();
-    }
-
-    @Test
-    public void hasNoValidRoundedCorners_zeroRadius() {
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(0).build();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 0);
-
-        assertThat(calculatedValidity).isFalse();
-    }
-
-    @Test
-    public void hasValidRoundedCorners_radiusDp() {
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 0);
-
-        assertThat(calculatedValidity).isTrue();
-    }
-
-    @Test
-    public void hasValidRoundedCorners_radiusPercentageOfHeight() {
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusPercentageOfHeight(20).build();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 0);
-
-        assertThat(calculatedValidity).isTrue();
-    }
-
-    @Test
-    public void hasValidRoundedCorners_radiusPercentageOfWidth() {
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusPercentageOfWidth(30).build();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 0);
-
-        assertThat(calculatedValidity).isTrue();
-    }
-
-    @Test
-    public void hasValidRoundedCorners_radiusOverride() {
-        RoundedCorners roundedCorners = RoundedCorners.getDefaultInstance();
-
-        boolean calculatedValidity = RoundedCornerViewHelper.hasValidRoundedCorners(
-                roundedCorners, /* radiusOverride= */ 10);
-
-        assertThat(calculatedValidity).isTrue();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerWrapperViewTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerWrapperViewTest.java
deleted file mode 100644
index 73a3a03..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerWrapperViewTest.java
+++ /dev/null
@@ -1,593 +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.
-
-package org.chromium.chrome.browser.feed.library.piet.ui;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.feed.library.common.functional.Suppliers;
-import org.chromium.chrome.browser.feed.library.piet.ui.RoundedCornerDelegateFactory.RoundingStrategy;
-import org.chromium.components.feed.core.proto.ui.piet.RoundedCornersProto.RoundedCorners;
-import org.chromium.components.feed.core.proto.ui.piet.StylesProto.Borders;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for the {@link RoundedCornerWrapperView}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class RoundedCornerWrapperViewTest {
-    private static final int TOP_CORNERS_BITMASK = 3;
-    private static final int LEFT_CORNERS_BITMASK = 9;
-    private static final int ALL_CORNERS_BITMASK = 15;
-
-    private Canvas mCanvas;
-    private Bitmap mBitmap;
-
-    @Before
-    public void setUp() {
-        mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
-        mCanvas = new Canvas(mBitmap);
-    }
-
-    @Test
-    public void layoutZeroDimensions_notRounded() {
-        RoundedCornerWrapperView view = CommonRoundedCornerWrapperView.getDefaultInstance();
-
-        view.layout(0, 0, 0, 100);
-        assertThat(view.getWidth()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-
-        view.layout(0, 0, 100, 0);
-        assertThat(view.getHeight()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-
-        view.layout(0, 0, 0, 0);
-        assertThat(view.getWidth()).isEqualTo(0);
-        assertThat(view.getHeight()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-    }
-
-    @Test
-    public void layoutZeroDimensions_rounded() {
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setBitmask(7).setRadiusDp(10).build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        view.layout(0, 0, 0, 100);
-        assertThat(view.getWidth()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-
-        view.layout(0, 0, 100, 0);
-        assertThat(view.getHeight()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-
-        view.layout(0, 0, 0, 0);
-        assertThat(view.getWidth()).isEqualTo(0);
-        assertThat(view.getHeight()).isEqualTo(0);
-        view.draw(mCanvas);
-        // Assert nothing fails
-    }
-
-    @Test
-    public void roundCorners_bordersAdded() {
-        // The width, height, and radius don't really matter for this test. As long as they exist, a
-        // BorderDrawable should be set on the view.
-        Borders borders = Borders.newBuilder().setWidth(10).build();
-
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
-        RoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                .setRoundedCorners(roundedCorners)
-                                                .setBorders(borders)
-                                                .build();
-
-        view.layout(0, 0, 100, 100);
-        view.draw(mCanvas);
-
-        // Assert that borders are created.
-        assertThat(view.getForeground()).isInstanceOf(BorderDrawable.class);
-        // The specifics of how the borders look are difficult to test here, and so are tested in a
-        // screendiff test instead.
-    }
-
-    @Test
-    public void setRoundedCorners_bordersWidthZero() {
-        Borders borders = Borders.newBuilder().setWidth(0).build();
-
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
-        RoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                .setRoundedCorners(roundedCorners)
-                                                .setBorders(borders)
-                                                .build();
-
-        // Set a width and height on the view so that the only thing stopping borders from being
-        // created is the border width of 0.
-        view.layout(0, 0, 100, 100);
-        view.draw(mCanvas);
-
-        // Assert that borders are not created.
-        assertThat(view.getForeground()).isNull();
-    }
-
-    @Test
-    public void getRadius_usesOverride() {
-        int viewWidth = 100;
-        int viewHeight = 100;
-        int radiusToSet = 10;
-        int radiusOverride = 20;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
-        RoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                .setRoundedCorners(roundedCorners)
-                                                .setRadiusOverride(radiusOverride)
-                                                .build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Make sure it's using the override value, NOT the radius from RoundedCorners.
-        assertThat(calculatedRadius).isEqualTo(radiusOverride);
-    }
-
-    @Test
-    public void getRadius_radiusDp() {
-        int viewWidth = 100;
-        int viewHeight = 100;
-        int radiusToSet = 10;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        assertThat(calculatedRadius).isEqualTo(radiusToSet);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfHeight() {
-        int viewWidth = 60;
-        int viewHeight = 100;
-        int radiusPercentageOfHeight = 25;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder()
-                        .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
-                        .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // 25% of the height is 25. .25 * 100 = 25
-        assertThat(calculatedRadius).isEqualTo(25);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfWidth() {
-        int viewWidth = 60;
-        int viewHeight = 100;
-        int radiusPercentageOfWidth = 25;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // 25% of the width is 15. .25 * 60 = 15
-        assertThat(calculatedRadius).isEqualTo(15);
-    }
-
-    @Test
-    public void getRadius_smallWidth_RadiusDpShouldAdjust() {
-        int viewWidth = 30;
-        int viewHeight = 40;
-        int radiusToSet = 20;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // A radius of 20 on each corner will add up to 40, which is bigger than the width. The
-        // radius should be adjusted to be 15.
-        assertThat(calculatedRadius).isEqualTo(15);
-    }
-
-    @Test
-    public void getRadius_smallWidth_RadiusDpShouldNotAdjust() {
-        int viewWidth = 30;
-        int viewHeight = 40;
-        int radiusToSet = 20;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(LEFT_CORNERS_BITMASK)
-                                                .setRadiusDp(radiusToSet)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // The radius of 20 is more than half the width. However, since only the left corners are
-        // rounded, the radius should not be adjusted, since 20dp is only half the height.
-        assertThat(calculatedRadius).isEqualTo(20);
-    }
-
-    @Test
-    public void getRadius_smallHeight_RadiusDpShouldAdjust() {
-        int viewWidth = 40;
-        int viewHeight = 30;
-        int radiusToSet = 20;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // A radius of 20 on each corner will add up to 40, which is bigger than the height. The
-        // radius should be adjusted to be 15.
-        assertThat(calculatedRadius).isEqualTo(15);
-    }
-
-    @Test
-    public void getRadius_smallHeight_RadiusDpShouldNotAdjust() {
-        int viewWidth = 40;
-        int viewHeight = 30;
-        int radiusToSet = 20;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(TOP_CORNERS_BITMASK)
-                                                .setRadiusDp(radiusToSet)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // The radius of 20 is more than half the height. However, since only the top corners are
-        // rounded, the radius should not be adjusted, since 20dp is only half the width.
-        assertThat(calculatedRadius).isEqualTo(20);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfHeight_100Percent_shouldAdjust() {
-        int viewWidth = 60;
-        int viewHeight = 100;
-        int radiusPercentageOfHeight = 100;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder()
-                        .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
-                        .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Since all corners are rounded, the radius should shrink to 30, half the width.
-        assertThat(calculatedRadius).isEqualTo(30);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfHeight_100Percent_topCorners_shouldAdjust() {
-        int viewWidth = 60;
-        int viewHeight = 100;
-        int radiusPercentageOfHeight = 100;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder()
-                        .setBitmask(TOP_CORNERS_BITMASK)
-                        .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
-                        .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Only top corners are rounded, but the radius should still shrink to 30, half the width,
-        // since the width is small.
-        assertThat(calculatedRadius).isEqualTo(30);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfHeight_100Percent_topCorners_shouldNotAdjust() {
-        int viewWidth = 100;
-        int viewHeight = 40;
-        int radiusPercentageOfHeight = 100;
-        RoundedCorners roundedCorners =
-                RoundedCorners.newBuilder()
-                        .setBitmask(TOP_CORNERS_BITMASK)
-                        .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
-                        .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Only top corners are rounded, and 40*2=80 is still less than the width (100), so we don't
-        // need to shrink the radius.
-        assertThat(calculatedRadius).isEqualTo(40);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfWidth_100Percent_shouldAdjust() {
-        int viewWidth = 100;
-        int viewHeight = 60;
-        int radiusPercentageOfWidth = 100;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Since all corners are rounded, the radius should shrink to 30, half the height, instead
-        // of 100% of 100 = 100.
-        assertThat(calculatedRadius).isEqualTo(30);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfWidth_100Percent_leftCorners_shouldAdjust() {
-        int viewWidth = 100;
-        int viewHeight = 60;
-        int radiusPercentageOfWidth = 100;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(LEFT_CORNERS_BITMASK)
-                                                .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Only left corners are rounded, but the radius should still shrink to 30, half the height,
-        // since the height is small. 100% of 100 = 100, which is greater than the height.
-        assertThat(calculatedRadius).isEqualTo(30);
-    }
-
-    @Test
-    public void getRadius_radiusPercentageOfWidth_100Percent_topCorners_shouldNotAdjust() {
-        int viewWidth = 40;
-        int viewHeight = 100;
-        int radiusPercentageOfWidth = 100;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(LEFT_CORNERS_BITMASK)
-                                                .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
-                                                .build();
-        RoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        int calculatedRadius = view.getRadius(viewWidth, viewHeight);
-
-        // Only left corners are rounded, and 40*2=80 is still less than the height (100), so we
-        // don't need to shrink the radius.
-        assertThat(calculatedRadius).isEqualTo(40);
-    }
-
-    @Test
-    public void useMaskingStrategy_notAllCornersRounded() {
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(LEFT_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view =
-                CommonRoundedCornerWrapperView.builder().setRoundedCorners(roundedCorners).build();
-
-        // When not all corners are rounded, the outline provider strategy doesn't work, so it
-        // should use bitmap masking
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.BITMAP_MASKING);
-    }
-
-    @Test
-    public void useOutlineProviderStrategy_hardwareAcceleration() {
-        int width = 100;
-        int height = 100;
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(ALL_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                      .setRoundedCorners(roundedCorners)
-                                                      .setHardwareAccelerated(true)
-                                                      .build();
-
-        // Lay out the view, because that's when the view would know if it's hardware accelerated.
-        view.layout(0, 0, width, height);
-
-        assertThat(view.getClipToOutline()).isTrue();
-        assertThat(view.getClipChildren()).isTrue();
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.OUTLINE_PROVIDER);
-    }
-
-    @Test
-    public void useClipPathStrategy_noHardwareAcceleration() {
-        int width = 100;
-        int height = 100;
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(ALL_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                      .setRoundedCorners(roundedCorners)
-                                                      .setHardwareAccelerated(false)
-                                                      .build();
-
-        view.layout(0, 0, width, height);
-
-        assertThat(view.getClipToOutline()).isFalse();
-        assertThat(view.getClipChildren()).isFalse();
-
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.CLIP_PATH);
-    }
-
-    @Test
-    public void useMaskingStrategy_noHardwareAcceleration_noClipPathAllowed() {
-        int width = 100;
-        int height = 100;
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(ALL_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                      .setRoundedCorners(roundedCorners)
-                                                      .setHardwareAccelerated(false)
-                                                      .setAllowClipPath(false)
-                                                      .build();
-
-        view.layout(0, 0, width, height);
-
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.BITMAP_MASKING);
-    }
-
-    @Test
-    public void useMaskingStrategy_withHwAcceleration_noClipPathAllowed_noOutlineProviderAllowed() {
-        int width = 100;
-        int height = 100;
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(ALL_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                      .setRoundedCorners(roundedCorners)
-                                                      .setHardwareAccelerated(true)
-                                                      .setAllowClipPath(false)
-                                                      .setAllowOutlineRounding(false)
-                                                      .build();
-
-        view.layout(0, 0, width, height);
-
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.BITMAP_MASKING);
-    }
-
-    @Test
-    public void allowAllRoundingStrategies_shouldChooseOutlineProvider() {
-        int width = 100;
-        int height = 100;
-        int radius = 16;
-        RoundedCorners roundedCorners = RoundedCorners.newBuilder()
-                                                .setBitmask(ALL_CORNERS_BITMASK)
-                                                .setRadiusDp(radius)
-                                                .build();
-        CommonRoundedCornerWrapperView view = CommonRoundedCornerWrapperView.builder()
-                                                      .setRoundedCorners(roundedCorners)
-                                                      .setHardwareAccelerated(true)
-                                                      .setAllowClipPath(true)
-                                                      .setAllowOutlineRounding(true)
-                                                      .build();
-
-        view.layout(0, 0, width, height);
-
-        assertThat(view.getClipToOutline()).isTrue();
-        assertThat(view.getClipChildren()).isTrue();
-
-        assertThat(view.getRoundingStrategy()).isEqualTo(RoundingStrategy.OUTLINE_PROVIDER);
-    }
-
-    static class CommonRoundedCornerWrapperView extends RoundedCornerWrapperView {
-        private static final Supplier<Boolean> IS_RTL_SUPPLIER = Suppliers.of(false);
-        private static final Context CONTEXT = Robolectric.buildActivity(Activity.class).get();
-        private final boolean mHardwareAccelerated;
-        private RoundingStrategy mRoundingStrategy;
-
-        @Override
-        public boolean isHardwareAccelerated() {
-            return mHardwareAccelerated;
-        }
-
-        @Override
-        public boolean isAttachedToWindow() {
-            return true;
-        }
-
-        @Override
-        void setRoundingDelegate(RoundingStrategy roundingStrategy) {
-            super.setRoundingDelegate(roundingStrategy);
-            this.mRoundingStrategy = roundingStrategy;
-        }
-
-        RoundingStrategy getRoundingStrategy() {
-            return mRoundingStrategy;
-        }
-
-        private CommonRoundedCornerWrapperView(Builder builder) {
-            super(CONTEXT, builder.mRoundedCorners, new RoundedCornerMaskCache(), IS_RTL_SUPPLIER,
-                    builder.mRadiusOverride, builder.mBorders, builder.mAllowClipPath,
-                    builder.mAllowOutlineRounding);
-            this.mHardwareAccelerated = builder.mHardwareAccelerated;
-        }
-
-        static CommonRoundedCornerWrapperView getDefaultInstance() {
-            return new CommonRoundedCornerWrapperView(new CommonRoundedCornerWrapperView.Builder());
-        }
-
-        static Builder builder() {
-            return new CommonRoundedCornerWrapperView.Builder();
-        }
-
-        static class Builder {
-            private boolean mHardwareAccelerated;
-
-            RoundedCorners mRoundedCorners = RoundedCorners.getDefaultInstance();
-            int mRadiusOverride;
-            Borders mBorders = Borders.getDefaultInstance();
-            boolean mAllowClipPath = true;
-            boolean mAllowOutlineRounding = true;
-
-            Builder setHardwareAccelerated(boolean value) {
-                mHardwareAccelerated = value;
-                return this;
-            }
-
-            Builder setRoundedCorners(RoundedCorners value) {
-                mRoundedCorners = value;
-                return this;
-            }
-
-            Builder setRadiusOverride(int value) {
-                mRadiusOverride = value;
-                return this;
-            }
-
-            Builder setBorders(Borders value) {
-                mBorders = value;
-                return this;
-            }
-
-            Builder setAllowClipPath(boolean value) {
-                mAllowClipPath = value;
-                return this;
-            }
-
-            Builder setAllowOutlineRounding(boolean value) {
-                mAllowOutlineRounding = value;
-                return this;
-            }
-
-            CommonRoundedCornerWrapperView build() {
-                return new CommonRoundedCornerWrapperView(this);
-            }
-        }
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contentchanged/StreamContentChangedListenerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contentchanged/StreamContentChangedListenerTest.java
deleted file mode 100644
index 763e8972..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contentchanged/StreamContentChangedListenerTest.java
+++ /dev/null
@@ -1,55 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.contentchanged;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests for {@link StreamContentChangedListener}.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamContentChangedListenerTest {
-    @Mock
-    ContentChangedListener mContentChangedListener;
-
-    StreamContentChangedListener mStreamContentChangedListener;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mStreamContentChangedListener = new StreamContentChangedListener();
-    }
-
-    @Test
-    public void testOnContentChanged() {
-        mStreamContentChangedListener.addContentChangedListener(mContentChangedListener);
-
-        mStreamContentChangedListener.onContentChanged();
-
-        verify(mContentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testRemoveContentChangedListener() {
-        mStreamContentChangedListener.addContentChangedListener(mContentChangedListener);
-        mStreamContentChangedListener.removeContentChangedListener(mContentChangedListener);
-
-        mStreamContentChangedListener.onContentChanged();
-
-        verify(mContentChangedListener, never()).onContentChanged();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/ContextMenuManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/ContextMenuManagerImplTest.java
deleted file mode 100644
index 36b0a11..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/ContextMenuManagerImplTest.java
+++ /dev/null
@@ -1,163 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.app.Activity;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager.ContextMenuClickHandler;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer.MenuMeasurer;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer.Size;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests for {@link ContextMenuManagerImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ContextMenuManagerImplTest {
-    private static final int NO_ID = -1;
-
-    @Mock
-    private MenuMeasurer mMenuMeasurer;
-    @Mock
-    private ContextMenuClickHandler mClickHandler;
-
-    private Activity mContext;
-    private ContextMenuManagerImpl mContextMenuManager;
-    private FrameLayout mParentView;
-    private View mAnchorView;
-    private List<String> mAdapterItems;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mContext.setTheme(R.style.Light);
-        mParentView = new FrameLayout(mContext);
-        mAnchorView = new View(mContext);
-        mParentView.addView(mAnchorView);
-        mContextMenuManager = new ContextMenuManagerImpl(mMenuMeasurer, mContext);
-        mContextMenuManager.setView(mParentView);
-        mAdapterItems = mCreateAdapterItems();
-        when(mMenuMeasurer.measureAdapterContent(any(ViewGroup.class),
-                     ArgumentMatchers.<ArrayAdapter<? extends Object>>any(), anyInt(), anyInt(),
-                     anyInt()))
-                .thenReturn(new Size(1, 2));
-    }
-
-    @Test
-    public void testGetHeight() {
-        mParentView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-
-        int top = 25;
-        int bottom = 150;
-        mParentView.layout(0, top, 0, bottom);
-
-        assertThat(mContextMenuManager.getStreamHeight()).isEqualTo(bottom - top);
-    }
-
-    @Test
-    public void testOpenContextMenu() {
-        mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler);
-        assertThat(shadowOf(mParentView).getDisallowInterceptTouchEvent()).isTrue();
-
-        PopupWindow popupWindow = shadowOf(mContext.getApplication()).getLatestPopupWindow();
-        ListView listView = (ListView) popupWindow.getContentView();
-
-        assertThat(listView.getAdapter().getItem(0)).isSameInstanceAs(mAdapterItems.get(0));
-        assertThat(listView.getAdapter().getItem(1)).isSameInstanceAs(mAdapterItems.get(1));
-        assertThat(listView.getAdapter().getItem(2)).isSameInstanceAs(mAdapterItems.get(2));
-
-        assertThat(popupWindow.isShowing()).isTrue();
-        listView.performItemClick(null, /* position= */ 1, NO_ID);
-
-        verify(mClickHandler).handleClick(1);
-        assertThat(popupWindow.isShowing()).isFalse();
-    }
-
-    @Test
-    public void testOnlyOpensOneMenu() {
-        // Opens first context menu.
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler))
-                .isTrue();
-
-        // Won't open a second one while the first one is open.
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler))
-                .isFalse();
-
-        shadowOf(mContext.getApplication()).getLatestPopupWindow().dismiss();
-
-        // After the menu is dismissed another can be opened
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler))
-                .isTrue();
-    }
-
-    @Test
-    public void testDismiss_fromLockingPhone() {
-        mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler);
-
-        assertThat(shadowOf(mContext.getApplication()).getLatestPopupWindow().isShowing()).isTrue();
-
-        mContextMenuManager.dismissPopup();
-
-        assertThat(shadowOf(mContext.getApplication()).getLatestPopupWindow().isShowing())
-                .isFalse();
-    }
-
-    @Test
-    public void testClosesMenuWhenDimensionsChange() {
-        mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler);
-
-        PopupWindow popupWindow = shadowOf(mContext.getApplication()).getLatestPopupWindow();
-        assertThat(popupWindow.isShowing()).isTrue();
-
-        mContextMenuManager.dismissPopup();
-
-        assertThat(popupWindow.isShowing()).isFalse();
-    }
-
-    @Test
-    public void testOpenContextMenu_hasShadow() {
-        mContextMenuManager.openContextMenu(mAnchorView, mAdapterItems, mClickHandler);
-
-        PopupWindow popupWindow = shadowOf(mContext.getApplication()).getLatestPopupWindow();
-        assertThat(popupWindow.getBackground()).isNotNull();
-    }
-
-    private List<String> mCreateAdapterItems() {
-        List<String> adapter = new ArrayList<>();
-        for (int i = 0; i < 3; i++) {
-            adapter.add(String.valueOf(i));
-        }
-        return adapter;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/FloatingContextMenuManagerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/FloatingContextMenuManagerTest.java
deleted file mode 100644
index 70543e82..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/FloatingContextMenuManagerTest.java
+++ /dev/null
@@ -1,126 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface.OnClickListener;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowAlertDialog;
-
-import org.chromium.chrome.browser.feed.library.sharedstream.contextmenumanager.ContextMenuManager.ContextMenuClickHandler;
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ContextMenuManagerImpl}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class FloatingContextMenuManagerTest {
-    private static final ImmutableList<String> ITEMS = ImmutableList.of("1", "2", "3");
-
-    private Context mContext;
-    private FloatingContextMenuManager mContextMenuManager;
-    @Mock
-    private ContextMenuClickHandler mHandler;
-    private View mAnchorView;
-
-    @Before
-    public void createContextMenuManager() {
-        initMocks(this);
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mContext.setTheme(R.style.Light);
-        mContextMenuManager = new FloatingContextMenuManager(mContext);
-        mAnchorView = new View(mContext);
-    }
-
-    @Test
-    public void openContextMenu_opensContextMenu() {
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull();
-    }
-
-    @Test
-    public void performClick_triggersClickHandler() {
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        performClick(0);
-
-        verify(mHandler).handleClick(0);
-    }
-
-    @Test
-    public void performClick_dismissesMenu() {
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        // Should dismiss the menu.
-        performClick(1);
-
-        // Should open a new menu, as the old one has been dismissed.
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler)).isTrue();
-    }
-
-    @Test
-    public void openContextMenu_multipleTimes_onlyOpensFirstMenu() {
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        // Fails to open as another menu is showing.
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, ImmutableList.of(), mHandler))
-                .isFalse();
-    }
-
-    @Test
-    public void openContextMenu_disallowsInterceptTouchEvent() {
-        FrameLayout parent = new FrameLayout(mContext);
-        parent.addView(mAnchorView);
-
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        assertThat(shadowOf(parent).getDisallowInterceptTouchEvent()).isTrue();
-    }
-
-    @Test
-    public void dismissPopup_dismissesPopup() {
-        mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler);
-
-        mContextMenuManager.dismissPopup();
-
-        assertThat(mContextMenuManager.openContextMenu(mAnchorView, ITEMS, mHandler)).isTrue();
-    }
-
-    /**
-     * Utility method to perform click on an index. Using the normal methods didn't seem to work,
-     * probably because we set the {@link ListView} via {@link AlertDialog.Builder#setView(View)}
-     * instead of {@link AlertDialog.Builder#setAdapter(ListAdapter, OnClickListener)} so it doesn't
-     * know that the view is a {@link ListView}
-     */
-    private void performClick(int index) {
-        AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
-
-        // Can't just use shadowAlertDialog.getListView, which returns null, as does performing the
-        // click on the shadow.
-        ListView listView = (ListView) shadowOf(dialog).getView();
-        listView.performItemClick(/* view= */ null, index, /* id= */ -1);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/deepestcontenttracker/DeepestContentTrackerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/deepestcontenttracker/DeepestContentTrackerTest.java
deleted file mode 100644
index be02622c..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/deepestcontenttracker/DeepestContentTrackerTest.java
+++ /dev/null
@@ -1,120 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.deepestcontenttracker;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link DeepestContentTracker}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class DeepestContentTrackerTest {
-    private static final String CONTENT_ID_1 = "CONTENT_ID_1";
-    private static final String CONTENT_ID_2 = "CONTENT_ID_2";
-    private static final String CONTENT_ID_3 = "CONTENT_ID_3";
-
-    private DeepestContentTracker mDeepestContentTracker;
-
-    @Before
-    public void setUp() {
-        mDeepestContentTracker = new DeepestContentTracker();
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker_updatesFromDeeperContent() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_2);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_2);
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker_doesNotAddContentIdIfAlreadyTracked() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_2);
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_2);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker_ignoresNullContentId() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(1, /* contentId= */ null);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker_updatingPreviousKnownPosition() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_2);
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_3);
-
-        assertThat(mDeepestContentTracker.getContentItAtPosition(0)).isEqualTo(CONTENT_ID_3);
-    }
-
-    @Test
-    public void testUpdateDeepestContentTracker_sparsePopulate() {
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_2);
-
-        assertThat(mDeepestContentTracker.getContentItAtPosition(0)).isNull();
-        assertThat(mDeepestContentTracker.getContentItAtPosition(1)).isEqualTo(CONTENT_ID_2);
-
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        assertThat(mDeepestContentTracker.getContentItAtPosition(0)).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testRemoveContentId() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_2);
-
-        mDeepestContentTracker.removeContentId(1);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testRemoveContentId_removingShallowerContentIdRetainsDeeperId() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-        mDeepestContentTracker.updateDeepestContentTracker(1, CONTENT_ID_2);
-
-        mDeepestContentTracker.removeContentId(0);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_2);
-    }
-
-    @Test
-    public void testRemoveContentId_doesNotRemoveContentId() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-
-        mDeepestContentTracker.removeContentId(1);
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isEqualTo(CONTENT_ID_1);
-    }
-
-    @Test
-    public void testReset() {
-        mDeepestContentTracker.updateDeepestContentTracker(0, CONTENT_ID_1);
-
-        mDeepestContentTracker.reset();
-
-        assertThat(mDeepestContentTracker.getChildViewDepth()).isNull();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/OneShotVisibilityLoggingListenerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/OneShotVisibilityLoggingListenerTest.java
deleted file mode 100644
index 1a47944..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/OneShotVisibilityLoggingListenerTest.java
+++ /dev/null
@@ -1,58 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.logging;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link OneShotVisibilityLoggingListener}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class OneShotVisibilityLoggingListenerTest {
-    @Mock
-    private LoggingListener mLoggingListener;
-
-    private OneShotVisibilityLoggingListener mOneShotVisibilityLoggingListener;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mOneShotVisibilityLoggingListener = new OneShotVisibilityLoggingListener(mLoggingListener);
-    }
-
-    @Test
-    public void testOnViewVisible() {
-        mOneShotVisibilityLoggingListener.onViewVisible();
-
-        verify(mLoggingListener).onViewVisible();
-    }
-
-    @Test
-    public void testOnViewVisible_onlyNotifiesListenerOnce() {
-        mOneShotVisibilityLoggingListener.onViewVisible();
-        reset(mLoggingListener);
-
-        mOneShotVisibilityLoggingListener.onViewVisible();
-
-        verify(mLoggingListener, never()).onViewVisible();
-    }
-
-    @Test
-    public void testOnContentClicked() {
-        mOneShotVisibilityLoggingListener.onContentClicked();
-
-        verify(mLoggingListener).onContentClicked();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/SpinnerLoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/SpinnerLoggerTest.java
deleted file mode 100644
index 4ad3fde0..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/SpinnerLoggerTest.java
+++ /dev/null
@@ -1,113 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.SpinnerType;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link SpinnerLogger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SpinnerLoggerTest {
-    private static final long START_TIME = 1000;
-    private static final int SPINNER_TIME = 500;
-
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    private FakeClock mClock;
-    private SpinnerLogger mSpinnerLogger;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mClock = new FakeClock();
-        mClock.set(START_TIME);
-
-        mSpinnerLogger = new SpinnerLogger(mBasicLoggingApi, mClock);
-    }
-
-    @Test
-    public void spinnerStarted_logsSpinnerStarted() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD);
-
-        verify(mBasicLoggingApi).onSpinnerStarted(SpinnerType.INITIAL_LOAD);
-    }
-
-    @Test
-    public void spinnerStarted_twice_throwsException() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD);
-
-        assertThatRunnable(() -> mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void spinnerFinished_logsOnSpinnerFinished_ifSpinnerStartedCalledBefore() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD);
-        mClock.advance(SPINNER_TIME);
-
-        mSpinnerLogger.spinnerFinished();
-
-        verify(mBasicLoggingApi).onSpinnerFinished(SPINNER_TIME, SpinnerType.INITIAL_LOAD);
-    }
-
-    @Test
-    public void spinnerFinished_beforeStarting_throwsException() {
-        assertThatRunnable(() -> mSpinnerLogger.spinnerFinished())
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void spinnerDestroyedWithoutCompleting_logsOnSpinnerDestroyedWithoutCompleting() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.MORE_BUTTON);
-        mClock.advance(SPINNER_TIME);
-
-        mSpinnerLogger.spinnerDestroyedWithoutCompleting();
-
-        verify(mBasicLoggingApi)
-                .onSpinnerDestroyedWithoutCompleting(SPINNER_TIME, SpinnerType.MORE_BUTTON);
-    }
-
-    @Test
-    public void spinnerDestroyed_beforeStarting_throwsException() {
-        assertThatRunnable(() -> mSpinnerLogger.spinnerDestroyedWithoutCompleting())
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void isSpinnerActive_returnsTrue_ifSpinnerStartedCalledBefore() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD);
-
-        assertThat(mSpinnerLogger.isSpinnerActive()).isTrue();
-    }
-
-    @Test
-    public void isSpinnerActive_returnsFalseInitially() {
-        assertThat(mSpinnerLogger.isSpinnerActive()).isFalse();
-    }
-
-    @Test
-    public void isSpinnerActive_returnsFalse_ifSpinnerFinishedCalledBefore() {
-        mSpinnerLogger.spinnerStarted(SpinnerType.INITIAL_LOAD);
-        mSpinnerLogger.spinnerFinished();
-
-        assertThat(mSpinnerLogger.isSpinnerActive()).isFalse();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/UiSessionRequestLoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/UiSessionRequestLoggerTest.java
deleted file mode 100644
index ea32966..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/UiSessionRequestLoggerTest.java
+++ /dev/null
@@ -1,147 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.SessionEvent;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelError.ErrorType;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelFeature;
-import org.chromium.chrome.browser.feed.library.testing.modelprovider.FakeModelProvider;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link UiSessionRequestLogger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class UiSessionRequestLoggerTest {
-    private static final long SESSION_EVENT_DELAY = 123L;
-    private static final ModelError MODEL_ERROR =
-            new ModelError(ErrorType.UNKNOWN, /* continuationToken= */ null);
-    private static final FakeModelFeature MODEL_FEATURE = FakeModelFeature.newBuilder().build();
-
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-
-    private FakeClock mClock;
-    private UiSessionRequestLogger mUiSessionRequestLogger;
-    private FakeModelProvider mModelProvider;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mClock = new FakeClock();
-        mModelProvider = new FakeModelProvider();
-        mUiSessionRequestLogger = new UiSessionRequestLogger(mClock, mBasicLoggingApi);
-    }
-
-    @Test
-    public void testOnSessionRequested_onSessionStart_logsSessionStart() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mClock.advance(SESSION_EVENT_DELAY);
-
-        mModelProvider.triggerOnSessionStart(MODEL_FEATURE);
-
-        verify(mBasicLoggingApi)
-                .onInitialSessionEvent(
-                        SessionEvent.STARTED, (int) SESSION_EVENT_DELAY, /* sessionCount= */ 1);
-        assertThat(mModelProvider.getObservers()).isEmpty();
-    }
-
-    @Test
-    public void testOnSessionRequested_onSessionFinished_logsSessionStart() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mClock.advance(SESSION_EVENT_DELAY);
-
-        mModelProvider.triggerOnSessionFinished();
-
-        verify(mBasicLoggingApi)
-                .onInitialSessionEvent(SessionEvent.FINISHED_IMMEDIATELY, (int) SESSION_EVENT_DELAY,
-                        /* sessionCount= */ 1);
-        assertThat(mModelProvider.getObservers()).isEmpty();
-    }
-
-    @Test
-    public void testOnSessionRequested_onSessionError_logsSessionStart() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mClock.advance(SESSION_EVENT_DELAY);
-
-        mModelProvider.triggerOnError(MODEL_ERROR);
-
-        verify(mBasicLoggingApi)
-                .onInitialSessionEvent(
-                        SessionEvent.ERROR, (int) SESSION_EVENT_DELAY, /* sessionCount= */ 1);
-        assertThat(mModelProvider.getObservers()).isEmpty();
-    }
-
-    @Test
-    public void testOnSessionRequested_onDestroy_logsDestroy() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mClock.advance(SESSION_EVENT_DELAY);
-
-        mUiSessionRequestLogger.onDestroy();
-
-        verify(mBasicLoggingApi)
-                .onInitialSessionEvent(SessionEvent.USER_ABANDONED, (int) SESSION_EVENT_DELAY,
-                        /* sessionCount= */ 1);
-        assertThat(mModelProvider.getObservers()).isEmpty();
-    }
-
-    @Test
-    public void testMultipleSessions_incrementsSessionCount() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mModelProvider.triggerOnSessionStart(MODEL_FEATURE);
-
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mModelProvider.triggerOnSessionFinished();
-
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-        mModelProvider.triggerOnError(MODEL_ERROR);
-
-        InOrder inOrder = inOrder(mBasicLoggingApi);
-
-        inOrder.verify(mBasicLoggingApi)
-                .onInitialSessionEvent(eq(SessionEvent.STARTED), anyInt(), eq(1));
-        inOrder.verify(mBasicLoggingApi)
-                .onInitialSessionEvent(eq(SessionEvent.FINISHED_IMMEDIATELY), anyInt(), eq(2));
-        inOrder.verify(mBasicLoggingApi)
-                .onInitialSessionEvent(eq(SessionEvent.ERROR), anyInt(), eq(3));
-    }
-
-    @Test
-    public void testOnSessionRequested_immediatelyTriggerSessionStart_logsSessionStart() {
-        mModelProvider.triggerOnSessionStartImmediately(MODEL_FEATURE);
-
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-
-        // The ModelProvider will sometimes trigger onSessionStart() immediately when a listener is
-        // registered. This tests that that situation is appropriately logged.
-        verify(mBasicLoggingApi).onInitialSessionEvent(SessionEvent.STARTED, 0, 1);
-    }
-
-    @Test
-    public void testOnSessionRequested_twice_deregistersFromFirstModelProvider() {
-        mUiSessionRequestLogger.onSessionRequested(mModelProvider);
-
-        mUiSessionRequestLogger.onSessionRequested(new FakeModelProvider());
-
-        assertThat(mModelProvider.getObservers()).isEmpty();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/VisibilityMonitorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/VisibilityMonitorTest.java
deleted file mode 100644
index 1b400f35..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/logging/VisibilityMonitorTest.java
+++ /dev/null
@@ -1,142 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.logging;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link VisibilityMonitor}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class VisibilityMonitorTest {
-    private static final Configuration CONFIGURATION =
-            new Configuration.Builder().put(ConfigKey.VIEW_LOG_THRESHOLD, .50).build();
-    private static final int VIEW_HEIGHT = 100;
-    private static final int VIEW_WIDTH = 50;
-
-    @Mock
-    private VisibilityListener mVisibilityListener;
-
-    private Activity mContext;
-    private ViewGroup mParentView;
-    private View mView;
-    private VisibilityMonitor mVisibilityMonitor;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mContext = Robolectric.setupActivity(Activity.class);
-
-        setUpViews(((child, r, offset) -> {
-            r.set(0, 0, VIEW_WIDTH, VIEW_HEIGHT);
-            return true;
-        }));
-    }
-
-    @Test
-    public void testPreDraw_notifiesListener() {
-        mContext.setContentView(mParentView);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener).onViewVisible();
-    }
-
-    @Test
-    public void testPreDraw_notifiesListenerIfVisibleViewHeightIsAtThreshold() {
-        setUpViews((child, r, offset) -> {
-            r.set(0, 0, VIEW_WIDTH, 50);
-            return true;
-        });
-        mContext.setContentView(mParentView);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener).onViewVisible();
-    }
-
-    @Test
-    public void testPreDraw_doesNotNotifyListenerIfAlreadyVisible() {
-        mContext.setContentView(mParentView);
-        mVisibilityMonitor.onPreDraw();
-        reset(mVisibilityListener);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener, never()).onViewVisible();
-    }
-
-    @Test
-    public void testPreDraw_doesNotNotifyListenerIfVisibleViewHeightIsBelowThreshold() {
-        setUpViews((child, r, offset) -> {
-            r.set(0, 0, VIEW_WIDTH, 49);
-            return true;
-        });
-        mContext.setContentView(mParentView);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener, never()).onViewVisible();
-    }
-
-    @Test
-    public void testPreDraw_doesNotNotifyListenerIfViewNotAttached() {
-        setUpViews((child, r, offset) -> false);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener, never()).onViewVisible();
-    }
-
-    @Test
-    public void testPreDraw_doesNotNotifyListenerIfParentIsNull() {
-        mVisibilityMonitor = new VisibilityMonitor(new FrameLayout(mContext), CONFIGURATION);
-        mVisibilityMonitor.setListener(null);
-
-        mVisibilityMonitor.onPreDraw();
-
-        verify(mVisibilityListener, never()).onViewVisible();
-    }
-
-    private void setUpViews(ChildVisibleRectMock childVisibleRectMock) {
-        mView = new FrameLayout(mContext);
-        mView.setLayoutParams(new LayoutParams(VIEW_WIDTH, VIEW_HEIGHT));
-        mParentView = new FrameLayout(mContext) {
-            @Override
-            public boolean getChildVisibleRect(View child, Rect r, Point offset) {
-                return childVisibleRectMock.getChildVisibleRect(child, r, offset);
-            }
-        };
-
-        mParentView.addView(mView);
-        mVisibilityMonitor = new VisibilityMonitor(mView, CONFIGURATION);
-        mVisibilityMonitor.setListener(mVisibilityListener);
-    }
-
-    interface ChildVisibleRectMock {
-        boolean getChildVisibleRect(View child, Rect r, Point offset);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/offlinemonitor/StreamOfflineMonitorTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/offlinemonitor/StreamOfflineMonitorTest.java
deleted file mode 100644
index 9fa629f2..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/offlinemonitor/StreamOfflineMonitorTest.java
+++ /dev/null
@@ -1,177 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.host.offlineindicator.OfflineIndicatorApi;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link StreamOfflineMonitor}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamOfflineMonitorTest {
-    private static final String URL = "google.com";
-
-    @Mock
-    private Consumer<Boolean> mOfflineStatusConsumer1;
-    @Mock
-    private Consumer<Boolean> mOfflineStatusConsumer2;
-    @Mock
-    private OfflineIndicatorApi mOfflineIndicatorApi;
-    @Captor
-    private ArgumentCaptor<List<String>> mListArgumentCaptor;
-
-    private StreamOfflineMonitor mStreamOfflineMonitor;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        mStreamOfflineMonitor = new StreamOfflineMonitor(mOfflineIndicatorApi);
-    }
-
-    @Test
-    public void testIsAvailableOffline() {
-        assertThat(mStreamOfflineMonitor.isAvailableOffline(URL)).isFalse();
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-        assertThat(mStreamOfflineMonitor.isAvailableOffline(URL)).isTrue();
-        mStreamOfflineMonitor.updateOfflineStatus(URL, false);
-        assertThat(mStreamOfflineMonitor.isAvailableOffline(URL)).isFalse();
-    }
-
-    @Test
-    public void testNotifyListeners() {
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-
-        verify(mOfflineStatusConsumer1).accept(true);
-
-        mStreamOfflineMonitor.updateOfflineStatus(URL, false);
-
-        verify(mOfflineStatusConsumer1).accept(false);
-    }
-
-    @Test
-    public void testNotify_multipleListeners() {
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer2);
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-
-        verify(mOfflineStatusConsumer1).accept(true);
-        verify(mOfflineStatusConsumer2).accept(true);
-    }
-
-    @Test
-    public void testRemoveConsumer() {
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-
-        mStreamOfflineMonitor.removeOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-
-        verify(mOfflineStatusConsumer1, never()).accept(anyBoolean());
-    }
-
-    @Test
-    public void testNotifyOnlyOnce() {
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-        mStreamOfflineMonitor.updateOfflineStatus(URL, false);
-        mStreamOfflineMonitor.updateOfflineStatus(URL, false);
-
-        verify(mOfflineStatusConsumer1, times(1)).accept(true);
-        verify(mOfflineStatusConsumer1, times(1)).accept(false);
-    }
-
-    @Test
-    public void testRequestOfflineStatusForNewContent() {
-        String url1 = "gmail.com";
-        String url2 = "mail.google.com";
-
-        // Checking if they are offline adds them to the list of articles the StreamOfflineMonitor
-        // will ask about.
-        mStreamOfflineMonitor.isAvailableOffline(URL);
-        mStreamOfflineMonitor.isAvailableOffline(url1);
-        mStreamOfflineMonitor.isAvailableOffline(url2);
-
-        mStreamOfflineMonitor.requestOfflineStatusForNewContent();
-        verify(mOfflineIndicatorApi)
-                .getOfflineStatus(mListArgumentCaptor.capture(),
-                        ArgumentMatchers.<Consumer<List<String>>>any());
-
-        assertThat(mListArgumentCaptor.getValue())
-                .containsExactlyElementsIn(Arrays.asList(URL, url1, url2));
-    }
-
-    @Test
-    public void testRequestOfflineStatusForNewContent_onlyRequestsOnce() {
-        // Checking if it is offline adds it to the list of articles the StreamOfflineMonitor will
-        // ask about.
-        mStreamOfflineMonitor.isAvailableOffline(URL);
-
-        mStreamOfflineMonitor.requestOfflineStatusForNewContent();
-        reset(mOfflineIndicatorApi);
-
-        mStreamOfflineMonitor.requestOfflineStatusForNewContent();
-
-        verify(mOfflineIndicatorApi, never())
-                .getOfflineStatus(eq(Collections.emptyList()),
-                        ArgumentMatchers.<Consumer<List<String>>>any());
-    }
-
-    @Test
-    public void testRequestOfflineStatusForNewContent_noUrls() {
-        mStreamOfflineMonitor.requestOfflineStatusForNewContent();
-
-        verify(mOfflineIndicatorApi, never())
-                .getOfflineStatus(eq(Collections.emptyList()),
-                        ArgumentMatchers.<Consumer<List<String>>>any());
-    }
-
-    @Test
-    public void testOnDestroy_clearsConsumersMap() {
-        mStreamOfflineMonitor.addOfflineStatusConsumer(URL, mOfflineStatusConsumer1);
-
-        // Should clear out all listeners
-        mStreamOfflineMonitor.onDestroy();
-
-        mStreamOfflineMonitor.updateOfflineStatus(URL, true);
-
-        verifyZeroInteractions(mOfflineStatusConsumer1);
-    }
-
-    @Test
-    public void testOnDestroy_detachesFromApi() {
-        mStreamOfflineMonitor.onDestroy();
-
-        verify(mOfflineIndicatorApi).removeOfflineStatusListener(mStreamOfflineMonitor);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietCustomElementProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietCustomElementProviderTest.java
deleted file mode 100644
index 406b12cf..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietCustomElementProviderTest.java
+++ /dev/null
@@ -1,84 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.host.CustomElementProvider;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.CustomElementData;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PietCustomElementProvider}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietCustomElementProviderTest {
-    @Mock
-    private CustomElementProvider mHostCustomElementProvider;
-
-    private Context mContext;
-    private View mHostCreatedView;
-    private PietCustomElementProvider mCustomElementProvider;
-    private PietCustomElementProvider mDelegatingCustomElementProvider;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mHostCreatedView = new View(mContext);
-        mCustomElementProvider =
-                new PietCustomElementProvider(mContext, /* customElementProvider */ null);
-        mDelegatingCustomElementProvider =
-                new PietCustomElementProvider(mContext, mHostCustomElementProvider);
-    }
-
-    @Test
-    public void testCreateCustomElement_noHostDelegate() {
-        View view =
-                mCustomElementProvider.createCustomElement(CustomElementData.getDefaultInstance());
-        assertThat(view).isNotNull();
-    }
-
-    @Test
-    public void testCreateCustomElement_hostDelegate() {
-        CustomElementData customElementData = CustomElementData.getDefaultInstance();
-        when(mHostCustomElementProvider.createCustomElement(customElementData))
-                .thenReturn(mHostCreatedView);
-
-        View view = mDelegatingCustomElementProvider.createCustomElement(customElementData);
-        assertThat(view).isEqualTo(mHostCreatedView);
-    }
-
-    @Test
-    public void testReleaseView_noHostDelegate() {
-        mCustomElementProvider.releaseCustomView(
-                new View(mContext), CustomElementData.getDefaultInstance());
-
-        // Above call should not throw
-    }
-
-    @Test
-    public void testReleaseView_hostDelegate() {
-        CustomElementData customElementData = CustomElementData.newBuilder().build();
-        mDelegatingCustomElementProvider.releaseCustomView(mHostCreatedView, customElementData);
-
-        verify(mHostCustomElementProvider).releaseCustomView(mHostCreatedView, customElementData);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietEventLoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietEventLoggerTest.java
deleted file mode 100644
index 335f849..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietEventLoggerTest.java
+++ /dev/null
@@ -1,34 +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.
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.components.feed.core.proto.ui.piet.ErrorsProto.ErrorCode;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Collections;
-
-/** Tests for {@link PietEventLogger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietEventLoggerTest {
-    @Test
-    public void testLogging() {
-        BasicLoggingApi basicLoggingApi = mock(BasicLoggingApi.class);
-        PietEventLogger logger = new PietEventLogger(basicLoggingApi);
-
-        logger.logEvents(Collections.singletonList(ErrorCode.ERR_MISSING_BINDING_VALUE));
-
-        verify(basicLoggingApi)
-                .onPietFrameRenderingEvent(
-                        Collections.singletonList(ErrorCode.ERR_MISSING_BINDING_VALUE_VALUE));
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietHostBindingProviderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietHostBindingProviderTest.java
deleted file mode 100644
index c4dea35..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietHostBindingProviderTest.java
+++ /dev/null
@@ -1,255 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.host.HostBindingProvider;
-import org.chromium.chrome.browser.feed.library.sharedstream.offlinemonitor.StreamOfflineMonitor;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.BindingValue;
-import org.chromium.components.feed.core.proto.ui.piet.ElementsProto.HostBindingData;
-import org.chromium.components.feed.core.proto.ui.piet.TextProto.ParameterizedText;
-import org.chromium.components.feed.core.proto.ui.stream.StreamOfflineExtensionProto.OfflineExtension;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PietHostBindingProvider}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietHostBindingProviderTest {
-    private static final String URL = "url";
-    private static final BindingValue OFFLINE_BINDING_VISIBILITY =
-            BindingValue.newBuilder().setBindingId("offline").build();
-    private static final BindingValue NOT_OFFLINE_BINDING_VISIBILITY =
-            BindingValue.newBuilder().setBindingId("not-offline").build();
-    public static final BindingValue OFFLINE_INDICATOR_BINDING =
-            BindingValue.newBuilder()
-                    .setHostBindingData(HostBindingData.newBuilder().setExtension(
-                            OfflineExtension.offlineExtension,
-                            OfflineExtension.newBuilder()
-                                    .setUrl(URL)
-                                    .setOfflineBinding(OFFLINE_BINDING_VISIBILITY)
-                                    .setNotOfflineBinding(NOT_OFFLINE_BINDING_VISIBILITY)
-                                    .build()))
-                    .build();
-
-    @Mock
-    private HostBindingProvider mHostHostBindingProvider;
-    @Mock
-    private StreamOfflineMonitor mOfflineMonitor;
-
-    private static final ParameterizedText TEXT_PAYLOAD =
-            ParameterizedText.newBuilder().setText("foo").build();
-
-    private static final BindingValue BINDING_WITH_HOST_DATA =
-            BindingValue.newBuilder()
-                    .setHostBindingData(HostBindingData.newBuilder())
-                    .setParameterizedText(TEXT_PAYLOAD)
-                    .build();
-    private static final BindingValue BINDING_WITHOUT_HOST_DATA =
-            BindingValue.newBuilder().setParameterizedText(TEXT_PAYLOAD).build();
-
-    private PietHostBindingProvider mHostBindingProvider;
-    private PietHostBindingProvider mDelegatingHostBindingProvider;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        mHostBindingProvider =
-                new PietHostBindingProvider(/* hostBindingProvider */ null, mOfflineMonitor);
-        mDelegatingHostBindingProvider =
-                new PietHostBindingProvider(mHostHostBindingProvider, mOfflineMonitor);
-    }
-
-    @Test
-    public void testGetCustomElementDataBindingForValue() {
-        assertThat(mHostBindingProvider.getCustomElementDataBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetCustomElementDataBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("custom-element").build();
-        when(mHostHostBindingProvider.getCustomElementDataBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getCustomElementDataBindingForValue(
-                           BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetParameterizedTextBindingForValue() {
-        assertThat(mHostBindingProvider.getParameterizedTextBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetParameterizedTextBindingForValue_delegating() {
-        BindingValue hostBinding =
-                BindingValue.newBuilder().setBindingId("parameterized-text").build();
-        when(mHostHostBindingProvider.getParameterizedTextBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getParameterizedTextBindingForValue(
-                           BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetChunkedTextBindingForValue() {
-        assertThat(mHostBindingProvider.getChunkedTextBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetChunkedTextBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("chunked-text").build();
-        when(mHostHostBindingProvider.getChunkedTextBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getChunkedTextBindingForValue(
-                           BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetImageBindingForValue() {
-        assertThat(mHostBindingProvider.getChunkedTextBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetImageBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("image").build();
-        when(mHostHostBindingProvider.getImageBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getImageBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetActionsBindingForValue() {
-        assertThat(mHostBindingProvider.getActionsBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetActionsBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("actions").build();
-        when(mHostHostBindingProvider.getActionsBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getActionsBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetGridCellWidthBindingForValue() {
-        assertThat(mHostBindingProvider.getGridCellWidthBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetGridCellWidthBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("gridcell").build();
-        when(mHostHostBindingProvider.getGridCellWidthBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getGridCellWidthBindingForValue(
-                           BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetLogDataBindingForValue() {
-        assertThat(mHostBindingProvider.getLogDataBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetLogDataBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("ved").build();
-        when(mHostHostBindingProvider.getLogDataBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getLogDataBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetTemplateBindingForValue() {
-        assertThat(mHostBindingProvider.getTemplateBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetTemplateBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("ved").build();
-        when(mHostHostBindingProvider.getTemplateBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(
-                mDelegatingHostBindingProvider.getTemplateBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetStyleBindingForValue() {
-        assertThat(mHostBindingProvider.getTemplateBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetStyleBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("ved").build();
-        when(mHostHostBindingProvider.getStyleBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(mDelegatingHostBindingProvider.getStyleBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetVisibilityBindingForValue_delegating() {
-        BindingValue hostBinding = BindingValue.newBuilder().setBindingId("ved").build();
-        when(mHostHostBindingProvider.getVisibilityBindingForValue(BINDING_WITH_HOST_DATA))
-                .thenReturn(hostBinding);
-
-        assertThat(
-                mDelegatingHostBindingProvider.getVisibilityBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(hostBinding);
-    }
-
-    @Test
-    public void testGetVisibilityBindingForValue_noOfflineExtension() {
-        assertThat(mHostBindingProvider.getVisibilityBindingForValue(BINDING_WITH_HOST_DATA))
-                .isEqualTo(BINDING_WITHOUT_HOST_DATA);
-    }
-
-    @Test
-    public void testGetVisibilityBindingForValue_offlineExtension_notOffline() {
-        when(mOfflineMonitor.isAvailableOffline(URL)).thenReturn(false);
-        assertThat(mHostBindingProvider.getVisibilityBindingForValue(OFFLINE_INDICATOR_BINDING))
-                .isEqualTo(NOT_OFFLINE_BINDING_VISIBILITY);
-    }
-
-    @Test
-    public void testGetVisibilityBindingForValue_offlineExtension_offline() {
-        when(mOfflineMonitor.isAvailableOffline(URL)).thenReturn(true);
-        assertThat(mHostBindingProvider.getVisibilityBindingForValue(OFFLINE_INDICATOR_BINDING))
-                .isEqualTo(OFFLINE_BINDING_VISIBILITY);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietImageLoaderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietImageLoaderTest.java
deleted file mode 100644
index 488cb587..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietImageLoaderTest.java
+++ /dev/null
@@ -1,74 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.graphics.drawable.Drawable;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Consumer;
-import org.chromium.chrome.browser.feed.library.api.host.imageloader.ImageLoaderApi;
-import org.chromium.chrome.browser.feed.library.api.host.stream.CardConfiguration;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.Image;
-import org.chromium.components.feed.core.proto.ui.piet.ImagesProto.ImageSource;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Tests for {@link PietImageLoader}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietImageLoaderTest {
-    private static final int DEFAULT_CORNER_RADIUS = 10;
-    private static final int WIDTH_PX = 50;
-    private static final int HEIGHT_PX = 150;
-
-    @Mock
-    private CardConfiguration mCardConfiguration;
-    @Mock
-    private ImageLoaderApi mImageLoaderApi;
-
-    private FakeClock mClock;
-    private PietImageLoader mPietImageLoader;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        when(mCardConfiguration.getDefaultCornerRadius()).thenReturn(DEFAULT_CORNER_RADIUS);
-
-        mClock = new FakeClock();
-        mClock.set(TimeUnit.MINUTES.toMillis(1));
-        mPietImageLoader = new PietImageLoader(mImageLoaderApi);
-    }
-
-    @Test
-    public void testGetImage() {
-        List<String> urls = Arrays.asList("url0", "url1", "url2");
-        Image.Builder imageBuilder = Image.newBuilder();
-        for (String url : urls) {
-            imageBuilder.addSources(ImageSource.newBuilder().setUrl(url).build());
-        }
-
-        Consumer<Drawable> consumer = value
-                -> {
-                        // Do nothing.
-                };
-
-        mPietImageLoader.getImage(imageBuilder.build(), WIDTH_PX, HEIGHT_PX, consumer);
-        verify(mImageLoaderApi).loadDrawable(urls, WIDTH_PX, HEIGHT_PX, consumer);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietRequiredContentAdapterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietRequiredContentAdapterTest.java
deleted file mode 100644
index a71de17..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietRequiredContentAdapterTest.java
+++ /dev/null
@@ -1,77 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.PietContent;
-import org.chromium.components.feed.core.proto.wire.ContentIdProto.ContentId;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation;
-import org.chromium.components.feed.core.proto.wire.DataOperationProto.DataOperation.Operation;
-import org.chromium.components.feed.core.proto.wire.FeatureProto.Feature;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PietRequiredContentAdapter}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietRequiredContentAdapterTest {
-    private static final ContentId CONTENT_ID_1 = ContentId.newBuilder().setId(1).build();
-    private static final ContentId CONTENT_ID_2 = ContentId.newBuilder().setId(2).build();
-    private static final Feature FEATURE =
-            Feature.newBuilder()
-                    .setExtension(Content.contentExtension,
-                            Content.newBuilder()
-                                    .setExtension(PietContent.pietContentExtension,
-                                            PietContent.newBuilder()
-                                                    .addPietSharedStates(CONTENT_ID_1)
-                                                    .addPietSharedStates(CONTENT_ID_2)
-                                                    .build())
-                                    .build())
-                    .build();
-
-    private final PietRequiredContentAdapter mAdapter = new PietRequiredContentAdapter();
-
-    @Test
-    public void testDetermineRequiredContentIds() {
-        assertThat(mAdapter.determineRequiredContentIds(
-                           DataOperation.newBuilder()
-                                   .setOperation(Operation.UPDATE_OR_APPEND)
-                                   .setFeature(FEATURE)
-                                   .build()))
-                .containsExactly(CONTENT_ID_1, CONTENT_ID_2);
-    }
-
-    @Test
-    public void testDetermineRequiredContentIds_removeDoesNotRequireContent() {
-        assertThat(mAdapter.determineRequiredContentIds(DataOperation.newBuilder()
-                                                                .setOperation(Operation.REMOVE)
-                                                                .setFeature(FEATURE)
-                                                                .build()))
-                .isEmpty();
-    }
-
-    @Test
-    public void testDetermineRequiredContentIds_clearAllDoesNotRequireContent() {
-        assertThat(mAdapter.determineRequiredContentIds(DataOperation.newBuilder()
-                                                                .setOperation(Operation.CLEAR_ALL)
-                                                                .setFeature(FEATURE)
-                                                                .build()))
-                .isEmpty();
-    }
-
-    @Test
-    public void testDetermineRequiredContentIds_defaultInstanceDoesNotRequireContent() {
-        assertThat(mAdapter.determineRequiredContentIds(
-                           DataOperation.newBuilder()
-                                   .setOperation(Operation.UPDATE_OR_APPEND)
-                                   .build()))
-                .isEmpty();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietStringFormatterTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietStringFormatterTest.java
deleted file mode 100644
index 8af60399..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/piet/PietStringFormatterTest.java
+++ /dev/null
@@ -1,26 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.piet;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-
-import java.util.concurrent.TimeUnit;
-
-/** Tests the {@link PietStringFormatter} */
-public class PietStringFormatterTest {
-    @Test
-    public void testGetRelativeElapsedString() {
-        FakeClock clock = new FakeClock();
-        clock.set(TimeUnit.MINUTES.toMillis(1));
-
-        PietStringFormatter pietStringFormatter = new PietStringFormatter(clock);
-        assertThat(pietStringFormatter.getRelativeElapsedString(TimeUnit.MINUTES.toMillis(1)))
-                .isEqualTo("1 minute ago");
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/publicapi/menumeasurer/MenuMeasurerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/publicapi/menumeasurer/MenuMeasurerTest.java
deleted file mode 100644
index 9147b56..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/publicapi/menumeasurer/MenuMeasurerTest.java
+++ /dev/null
@@ -1,66 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer.MenuMeasurer.NO_MAX_HEIGHT;
-import static org.chromium.chrome.browser.feed.library.sharedstream.publicapi.menumeasurer.MenuMeasurer.NO_MAX_WIDTH;
-
-import android.app.Activity;
-
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.R;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link MenuMeasurer}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class MenuMeasurerTest {
-    private static final int NO_PADDING = 0;
-
-    private MenuMeasurer mMenuMeasurer;
-    private int mWidthUnit;
-
-    @Before
-    public void setup() {
-        Activity activity = Robolectric.buildActivity(Activity.class).create().visible().get();
-        mMenuMeasurer = new MenuMeasurer(activity);
-        mWidthUnit = activity.getResources().getDimensionPixelSize(R.dimen.menu_width_multiple);
-    }
-
-    @Test
-    public void testCalculateSize() {
-        assertThat(
-                mMenuMeasurer.calculateSize(Lists.newArrayList(new Size(10, 20), new Size(10, 20)),
-                        NO_PADDING, NO_MAX_WIDTH, NO_MAX_HEIGHT))
-                .isEqualTo(new Size(roundToWidthUnit(10), 40));
-    }
-
-    @Test
-    public void testCalculateSize_maxWidth() {
-        assertThat(mMenuMeasurer.calculateSize(
-                           Lists.newArrayList(new Size(100, 100)), NO_PADDING, 70, NO_MAX_HEIGHT))
-                .isEqualTo(new Size(70, 100));
-    }
-
-    @Test
-    public void testCalculateSize_maxHeight() {
-        assertThat(mMenuMeasurer.calculateSize(
-                           Lists.newArrayList(new Size(100, 100)), NO_PADDING, NO_MAX_WIDTH, 60))
-                .isEqualTo(new Size(roundToWidthUnit(100), 60));
-    }
-
-    private int roundToWidthUnit(int measuredWidth) {
-        return Math.round((((float) measuredWidth / (float) mWidthUnit) + 0.5f)) * mWidthUnit;
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/removetrackingfactory/StreamRemoveTrackingFactoryTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/removetrackingfactory/StreamRemoveTrackingFactoryTest.java
deleted file mode 100644
index 0ee992b..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/removetrackingfactory/StreamRemoveTrackingFactoryTest.java
+++ /dev/null
@@ -1,128 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.removetrackingfactory;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.ContentRemoval;
-import org.chromium.chrome.browser.feed.library.api.client.knowncontent.KnownContent;
-import org.chromium.chrome.browser.feed.library.api.common.MutationContext;
-import org.chromium.chrome.browser.feed.library.api.internal.knowncontent.FeedKnownContent;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.ModelProvider;
-import org.chromium.chrome.browser.feed.library.api.internal.modelprovider.RemoveTracking;
-import org.chromium.components.feed.core.proto.libraries.api.internal.StreamDataProto.StreamFeature;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.Content;
-import org.chromium.components.feed.core.proto.ui.stream.StreamStructureProto.RepresentationData;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-import java.util.List;
-
-/** Tests for {@link StreamRemoveTrackingFactory}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class StreamRemoveTrackingFactoryTest {
-    private static final String SESSION_ID = "sessionId";
-    private static final String URL1 = "url1";
-    private static final String URL2 = "url2";
-
-    @Mock
-    private FeedKnownContent mFeedKnownContent;
-    @Mock
-    private ModelProvider mModelProvider;
-    @Mock
-    private KnownContent.Listener mKnownContentListener;
-
-    private StreamRemoveTrackingFactory mStreamRemoveTrackingFactory;
-
-    @Before
-    public void setup() {
-        initMocks(this);
-        when(mModelProvider.getSessionId()).thenReturn(SESSION_ID);
-        when(mFeedKnownContent.getKnownContentHostNotifier()).thenReturn(mKnownContentListener);
-        mStreamRemoveTrackingFactory =
-                new StreamRemoveTrackingFactory(mModelProvider, mFeedKnownContent);
-    }
-
-    @Test
-    public void testCreate_nullRequestingSession() {
-        assertThat(mStreamRemoveTrackingFactory.create(MutationContext.EMPTY_CONTEXT)).isNull();
-    }
-
-    @Test
-    public void testCreate_otherSessionId() {
-        assertThat(mStreamRemoveTrackingFactory.create(
-                           new MutationContext.Builder().setRequestingSessionId("otherId").build()))
-                .isNull();
-    }
-
-    @Test
-    public void testRemoveTracking_userInitiated() {
-        List<ContentRemoval> removedContents = triggerConsumerUpdatesFor(
-                /* isUserInitiated= */ true, buildStreamFeatureForUrl(URL2),
-                StreamFeature.getDefaultInstance(), buildStreamFeatureForUrl(URL1),
-                StreamFeature.getDefaultInstance());
-
-        assertThat(removedContents).hasSize(2);
-        assertThat(removedContents.get(0).getUrl()).isEqualTo(URL2);
-        assertThat(removedContents.get(1).getUrl()).isEqualTo(URL1);
-        assertThat(removedContents.get(0).isRequestedByUser()).isTrue();
-    }
-
-    @Test
-    public void testRemoveTracking_notUserInitiated() {
-        List<ContentRemoval> removedContents = triggerConsumerUpdatesFor(
-                /* isUserInitiated= */ false, buildStreamFeatureForUrl(URL1),
-                buildStreamFeatureForUrl(URL2), StreamFeature.getDefaultInstance());
-
-        assertThat(removedContents).hasSize(2);
-        assertThat(removedContents.get(0).getUrl()).isEqualTo(URL1);
-        assertThat(removedContents.get(1).getUrl()).isEqualTo(URL2);
-        assertThat(removedContents.get(0).isRequestedByUser()).isFalse();
-    }
-
-    @SuppressWarnings("unchecked")
-    private List<ContentRemoval> triggerConsumerUpdatesFor(
-            boolean isUserInitiated, StreamFeature... streamFeatures) {
-        RemoveTracking<ContentRemoval> contentRemovalRemoveTracking =
-                mStreamRemoveTrackingFactory.create(createMutationContext(isUserInitiated));
-
-        for (StreamFeature streamFeature : streamFeatures) {
-            contentRemovalRemoveTracking.filterStreamFeature(streamFeature);
-        }
-
-        contentRemovalRemoveTracking.triggerConsumerUpdate();
-
-        ArgumentCaptor<List<ContentRemoval>> removedContentCaptor =
-                (ArgumentCaptor<List<ContentRemoval>>) (Object) ArgumentCaptor.forClass(List.class);
-
-        verify(mKnownContentListener).onContentRemoved(removedContentCaptor.capture());
-        return removedContentCaptor.getValue();
-    }
-
-    private StreamFeature buildStreamFeatureForUrl(String url) {
-        return StreamFeature.newBuilder()
-                .setContent(Content.newBuilder().setRepresentationData(
-                        RepresentationData.newBuilder().setUri(url)))
-                .build();
-    }
-
-    private MutationContext createMutationContext(boolean isUserInitiated) {
-        return new MutationContext.Builder()
-                .setRequestingSessionId(SESSION_ID)
-                .setUserInitiated(isUserInitiated)
-                .build();
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/PietScrollObserverTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/PietScrollObserverTest.java
deleted file mode 100644
index 81e72917..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/PietScrollObserverTest.java
+++ /dev/null
@@ -1,135 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.scroll;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.app.Activity;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.piet.FrameAdapter;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler;
-import org.chromium.chrome.browser.feed.library.piet.host.ActionHandler.ActionType;
-import org.chromium.chrome.browser.feed.library.piet.testing.FakeFrameAdapter;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObservable;
-import org.chromium.components.feed.core.proto.ui.piet.ActionsProto.Action;
-import org.chromium.components.feed.core.proto.ui.piet.PietProto.Frame;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link PietScrollObserverTest}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PietScrollObserverTest {
-    @Mock
-    private ScrollObservable mScrollObservable;
-    @Mock
-    private ActionHandler mActionHandler;
-
-    // .newBuilder().build() creates a unique instance we can check same() against.
-    private static final Action VIEW_ACTION = Action.newBuilder().build();
-
-    private FrameAdapter mFrameAdapter;
-    private FrameLayout mViewport;
-    private LinearLayout mFrameView;
-    private PietScrollObserver mPietScrollObserver;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        Activity activity = Robolectric.setupActivity(Activity.class);
-        mViewport = new FrameLayout(activity);
-        activity.getWindow().addContentView(mViewport, new LayoutParams(100, 100));
-
-        mFrameAdapter = FakeFrameAdapter.builder(activity)
-                                .setActionHandler(mActionHandler)
-                                .addViewAction(VIEW_ACTION)
-                                .build();
-        mFrameAdapter.bindModel(Frame.getDefaultInstance(), 0, null, ImmutableList.of());
-        mFrameView = mFrameAdapter.getFrameContainer();
-
-        mPietScrollObserver = new PietScrollObserver(mFrameAdapter, mViewport, mScrollObservable);
-    }
-
-    @Test
-    public void testTriggersActionsWhenIdle() {
-        mPietScrollObserver.onScrollStateChanged(
-                mViewport, "", RecyclerView.SCROLL_STATE_IDLE, 123L);
-
-        verify(mActionHandler)
-                .handleAction(same(VIEW_ACTION), eq(ActionType.VIEW),
-                        eq(Frame.getDefaultInstance()), eq(mFrameAdapter.getFrameContainer()),
-                        any() /* LogData */);
-    }
-
-    @Test
-    public void testDoesNotTriggerWhenScrolling() {
-        mPietScrollObserver.onScrollStateChanged(
-                mViewport, "", RecyclerView.SCROLL_STATE_DRAGGING, 123L);
-
-        verifyZeroInteractions(mActionHandler);
-    }
-
-    @Test
-    public void testTriggersOnFirstDraw() {
-        mPietScrollObserver.installFirstDrawTrigger();
-        when(mScrollObservable.getCurrentScrollState()).thenReturn(RecyclerView.SCROLL_STATE_IDLE);
-
-        mFrameView.getViewTreeObserver().dispatchOnGlobalLayout();
-
-        verify(mActionHandler)
-                .handleAction(same(VIEW_ACTION), eq(ActionType.VIEW),
-                        eq(Frame.getDefaultInstance()), eq(mFrameView), any() /* LogData */);
-    }
-
-    @Test
-    public void testDoesNotTriggerOnSecondDraw() {
-        mPietScrollObserver.installFirstDrawTrigger();
-        when(mScrollObservable.getCurrentScrollState())
-                .thenReturn(RecyclerView.SCROLL_STATE_DRAGGING);
-
-        // trigger on global layout - actions will not trigger because scrolling is not IDLE
-        mFrameView.getViewTreeObserver().dispatchOnGlobalLayout();
-
-        when(mScrollObservable.getCurrentScrollState()).thenReturn(RecyclerView.SCROLL_STATE_IDLE);
-
-        // trigger on global layout - actions will not trigger because observer has been removed.
-        mFrameView.getViewTreeObserver().dispatchOnGlobalLayout();
-
-        verifyZeroInteractions(mActionHandler);
-    }
-
-    @Test
-    public void testTriggersOnAttach() {
-        // Actions will not fire before attached to window
-        mFrameView.getViewTreeObserver().dispatchOnGlobalLayout();
-        verifyZeroInteractions(mActionHandler);
-
-        // Now attach to window and actions should fire
-        mViewport.addView(mFrameView);
-        mFrameView.getViewTreeObserver().dispatchOnGlobalLayout();
-        verify(mActionHandler)
-                .handleAction(same(VIEW_ACTION), eq(ActionType.VIEW),
-                        eq(Frame.getDefaultInstance()), eq(mFrameView), any() /* LogData */);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollListenerNotifierTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollListenerNotifierTest.java
deleted file mode 100644
index dd81b30..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollListenerNotifierTest.java
+++ /dev/null
@@ -1,174 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.scroll;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import static org.chromium.chrome.browser.feed.library.common.testing.RunnableSubject.assertThatRunnable;
-import static org.chromium.chrome.browser.feed.shared.stream.Stream.ScrollListener.UNKNOWN_SCROLL_DELTA;
-
-import android.app.Activity;
-import android.content.Context;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.common.concurrent.testing.FakeMainThreadRunner;
-import org.chromium.chrome.browser.feed.library.common.time.testing.FakeClock;
-import org.chromium.chrome.browser.feed.library.sharedstream.publicapi.scroll.ScrollObservable;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ScrollListener;
-import org.chromium.chrome.browser.feed.shared.stream.Stream.ScrollListener.ScrollState;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ScrollListenerNotifier}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class ScrollListenerNotifierTest {
-    private static final String FEATURE_ID = "feature";
-    private static final long TIME = 12345L;
-
-    @Mock
-    private ScrollListener mScrollListener1;
-    @Mock
-    private ScrollListener mScrollListener2;
-    @Mock
-    private ContentChangedListener mContentChangedListener;
-    @Mock
-    private ScrollObservable mScrollObservable;
-
-    private ScrollListenerNotifier mScrollListenerNotifier;
-    private RecyclerView mRecyclerView;
-    private FakeMainThreadRunner mMainThreadRunner;
-    private FakeClock mClock;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-
-        Context context = Robolectric.buildActivity(Activity.class).get();
-        mRecyclerView = new RecyclerView(context);
-        mMainThreadRunner = FakeMainThreadRunner.queueAllTasks();
-        mClock = new FakeClock();
-        mClock.set(TIME);
-
-        mScrollListenerNotifier = new ScrollListenerNotifier(
-                mContentChangedListener, mScrollObservable, mMainThreadRunner);
-        mScrollListenerNotifier.addScrollListener(mScrollListener1);
-    }
-
-    @Test
-    public void testConstructor() {
-        ScrollListenerNotifier notifier = new ScrollListenerNotifier(
-                mContentChangedListener, mScrollObservable, mMainThreadRunner);
-
-        verify(mScrollObservable).addScrollObserver(notifier);
-    }
-
-    @Test
-    public void testScrollStateOutputs() {
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_IDLE, TIME);
-        verify(mScrollListener1).onScrollStateChanged(ScrollState.IDLE);
-
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_DRAGGING, TIME);
-        verify(mScrollListener1).onScrollStateChanged(ScrollState.DRAGGING);
-
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_SETTLING, TIME);
-        verify(mScrollListener1).onScrollStateChanged(ScrollState.SETTLING);
-
-        assertThatRunnable(()
-                                   -> mScrollListenerNotifier.onScrollStateChanged(
-                                           mRecyclerView, FEATURE_ID, -42, TIME))
-                .throwsAnExceptionOfType(RuntimeException.class);
-    }
-
-    @Test
-    public void testOnScrollStateChanged() {
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_IDLE, TIME);
-
-        verify(mScrollListener1).onScrollStateChanged(ScrollState.IDLE);
-    }
-
-    @Test
-    public void testOnScrollStateChanged_notifiesObserverOnIdle() {
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_IDLE, TIME);
-
-        verify(mContentChangedListener).onContentChanged();
-    }
-
-    @Test
-    public void testOnScrollStateChanged_multipleListeners() {
-        mScrollListenerNotifier.addScrollListener(mScrollListener2);
-
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_IDLE, TIME);
-
-        verify(mScrollListener1).onScrollStateChanged(ScrollState.IDLE);
-        verify(mScrollListener2).onScrollStateChanged(ScrollState.IDLE);
-    }
-
-    @Test
-    public void testOnScrollStateChanged_removedListener() {
-        mScrollListenerNotifier.addScrollListener(mScrollListener2);
-        mScrollListenerNotifier.removeScrollListener(mScrollListener1);
-
-        mScrollListenerNotifier.onScrollStateChanged(
-                mRecyclerView, FEATURE_ID, RecyclerView.SCROLL_STATE_IDLE, TIME);
-
-        verify(mScrollListener1, never()).onScrollStateChanged(anyInt());
-        verify(mScrollListener2).onScrollStateChanged(ScrollState.IDLE);
-    }
-
-    @Test
-    public void testOnScrolled() {
-        mScrollListenerNotifier.onScroll(mRecyclerView, FEATURE_ID, 1, 2);
-
-        verify(mScrollListener1).onScrolled(1, 2);
-    }
-
-    @Test
-    public void testOnScrolled_multipleListeners() {
-        mScrollListenerNotifier.addScrollListener(mScrollListener2);
-        mScrollListenerNotifier.onScroll(mRecyclerView, FEATURE_ID, 1, 2);
-
-        verify(mScrollListener1).onScrolled(1, 2);
-        verify(mScrollListener2).onScrolled(1, 2);
-    }
-
-    @Test
-    public void testOnScrolled_removedListener() {
-        mScrollListenerNotifier.addScrollListener(mScrollListener2);
-        mScrollListenerNotifier.removeScrollListener(mScrollListener1);
-
-        mScrollListenerNotifier.onScroll(mRecyclerView, FEATURE_ID, 1, 2);
-
-        verify(mScrollListener1, never()).onScrolled(anyInt(), anyInt());
-        verify(mScrollListener2).onScrolled(1, 2);
-    }
-
-    @Test
-    public void onProgrammaticScroll() {
-        mScrollListenerNotifier.onProgrammaticScroll(mRecyclerView);
-
-        verify(mScrollListener1, never()).onScrolled(anyInt(), anyInt());
-
-        mMainThreadRunner.runAllTasks();
-        verify(mScrollListener1).onScrolled(UNKNOWN_SCROLL_DELTA, UNKNOWN_SCROLL_DELTA);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollLoggerTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollLoggerTest.java
deleted file mode 100644
index 8b37c58..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollLoggerTest.java
+++ /dev/null
@@ -1,48 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.scroll;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.logging.BasicLoggingApi;
-import org.chromium.chrome.browser.feed.library.api.host.logging.ScrollType;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/** Tests for {@link ScrollLogger}. */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class ScrollLoggerTest {
-    @Mock
-    private BasicLoggingApi mBasicLoggingApi;
-    private ScrollLogger mScrollLogger;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        mScrollLogger = new ScrollLogger(mBasicLoggingApi);
-    }
-
-    @Test
-    public void testLogsScroll() {
-        int scrollAmount = 100;
-        mScrollLogger.handleScroll(ScrollType.STREAM_SCROLL, scrollAmount);
-        verify(mBasicLoggingApi).onScroll(ScrollType.STREAM_SCROLL, scrollAmount);
-    }
-
-    @Test
-    public void testLogsNoScroll_tooSmall() {
-        int scrollAmount = 1;
-        mScrollLogger.handleScroll(ScrollType.STREAM_SCROLL, scrollAmount);
-        verify(mBasicLoggingApi, never()).onScroll(ScrollType.STREAM_SCROLL, scrollAmount);
-    }
-}
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollRestoreHelperTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollRestoreHelperTest.java
deleted file mode 100644
index e00f81d..0000000
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/scroll/ScrollRestoreHelperTest.java
+++ /dev/null
@@ -1,111 +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.
-
-package org.chromium.chrome.browser.feed.library.sharedstream.scroll;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration;
-import org.chromium.chrome.browser.feed.library.api.host.config.Configuration.ConfigKey;
-import org.chromium.chrome.browser.feed.library.testing.android.LinearLayoutManagerForTest;
-import org.chromium.components.feed.core.proto.libraries.sharedstream.ScrollStateProto.ScrollState;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class ScrollRestoreHelperTest {
-    private static final int HEADER_COUNT = 10;
-    private static final int ABANDON_RESTORE_BELOW_FOLD_THRESHOLD = 5;
-    private static final int FIRST_VISIBLE_ITEM_POSITION = 3;
-    private static final int FIRST_VISIBLE_ITEM_OFFSET = 14;
-
-    private LinearLayoutManagerForTest mLinearLayoutManager;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = Robolectric.buildActivity(Activity.class).get();
-        mLinearLayoutManager = new LinearLayoutManagerForTest(mContext);
-        mLinearLayoutManager.firstVisibleItemPosition = FIRST_VISIBLE_ITEM_POSITION;
-        mLinearLayoutManager.firstVisibleViewOffset = FIRST_VISIBLE_ITEM_OFFSET;
-    }
-
-    @Test
-    public void testGetScrollStateForScrollRestore_noFirstPosition_returnsNull() {
-        mLinearLayoutManager.firstVisibleItemPosition = RecyclerView.NO_POSITION;
-
-        assertThat(ScrollRestoreHelper.getScrollStateForScrollRestore(
-                           mLinearLayoutManager, new Configuration.Builder().build(), HEADER_COUNT))
-                .isNull();
-    }
-
-    @Test
-    public void testGetScrollStateForScrollRestore_noLastPosition_returnsNull() {
-        // configurationMap.put(ConfigKey.ABANDON_RESTORE_BELOW_FOLD, true);
-
-        mLinearLayoutManager.lastVisibleItemPosition = RecyclerView.NO_POSITION;
-
-        assertThat(ScrollRestoreHelper.getScrollStateForScrollRestore(
-                           mLinearLayoutManager, new Configuration.Builder().build(), HEADER_COUNT))
-                .isNull();
-    }
-
-    @Test
-    public void testGetScrollStateForScrollRestore_scrolledTooFar_returnsNull() {
-        Configuration configuration = new Configuration.Builder()
-                                              .put(ConfigKey.ABANDON_RESTORE_BELOW_FOLD, true)
-                                              .put(ConfigKey.ABANDON_RESTORE_BELOW_FOLD_THRESHOLD,
-                                                      (long) ABANDON_RESTORE_BELOW_FOLD_THRESHOLD)
-                                              .build();
-
-        setUpForRestoringBelowTheFold();
-        View view = new View(mContext);
-        view.setTop(FIRST_VISIBLE_ITEM_OFFSET);
-        mLinearLayoutManager.addChildToPosition(FIRST_VISIBLE_ITEM_POSITION, view);
-
-        // With ABANDON_RESTORE_BELOW_FOLD true, we should not restore below the fold.
-        assertThat(ScrollRestoreHelper.getScrollStateForScrollRestore(
-                           mLinearLayoutManager, configuration, HEADER_COUNT))
-                .isNull();
-    }
-
-    @Test
-    public void testGetScrollStateForScrollRestore_dontAbandonScrollRestore_returnsScrollState() {
-        Configuration configuration = new Configuration.Builder()
-                                              .put(ConfigKey.ABANDON_RESTORE_BELOW_FOLD, false)
-                                              .put(ConfigKey.ABANDON_RESTORE_BELOW_FOLD_THRESHOLD,
-                                                      (long) ABANDON_RESTORE_BELOW_FOLD_THRESHOLD)
-                                              .build();
-
-        setUpForRestoringBelowTheFold();
-        View view = new View(mContext);
-        view.setTop(FIRST_VISIBLE_ITEM_OFFSET);
-        mLinearLayoutManager.addChildToPosition(FIRST_VISIBLE_ITEM_POSITION, view);
-
-        // With ABANDON_RESTORE_BELOW_FOLD false, we should restore below the fold.
-        assertThat(ScrollRestoreHelper.getScrollStateForScrollRestore(
-                           mLinearLayoutManager, configuration, HEADER_COUNT))
-                .isEqualTo(ScrollState.newBuilder()
-                                   .setPosition(FIRST_VISIBLE_ITEM_POSITION)
-                                   .setOffset(FIRST_VISIBLE_ITEM_OFFSET)
-                                   .build());
-    }
-
-    private void setUpForRestoringBelowTheFold() {
-        mLinearLayoutManager.lastVisibleItemPosition =
-                HEADER_COUNT + ABANDON_RESTORE_BELOW_FOLD_THRESHOLD + 1;
-    }
-}
diff --git a/chrome/android/feed/merging.md b/chrome/android/feed/merging.md
deleted file mode 100644
index d6ce70a..0000000
--- a/chrome/android/feed/merging.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Manual Merges
-
-[Piet code](https://cs.chromium.org/chromium/src/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/piet/)
-and [feed wire protos](https://cs.chromium.org/chromium/src/components/feed/core/proto/wire/)
-are hand-merged from https://chromium.googlesource.com/feed git repo regularly.
-Commits that don't touch these files do not cause changes here.
-
-The hash below represents the last commit from that repo that was reviewed for
-the potential need to merge here.
-
-Last checked commit ID: c6eb65cc4e080d96c453c677df9bf58b3abcb9b8
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 23702226..49c743b9 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1046,6 +1046,15 @@
         <receiver android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"
             android:exported="false"/>
 
+        <activity
+            android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$TrampolineActivity"
+            android:theme="@android:style/Theme.NoDisplay"
+            android:exported="false"
+            android:autoRemoveFromRecents="true"
+            android:excludeFromRecents="true"
+            android:documentLaunchMode="always"
+            android:noHistory="true"/>
+
         <receiver android:name="org.chromium.chrome.browser.notifications.scheduler.DisplayAgent$Receiver"
             android:exported="false"/>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 9660219..f50192d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -407,7 +407,7 @@
     public ChromeTabbedActivity() {
         mMainIntentMetrics = new MainIntentBehaviorMetrics();
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-            mMultiInstanceManager = new MultiInstanceManager(this, mTabModelSelectorSupplier,
+            mMultiInstanceManager = new MultiInstanceManager(this, getTabModelSelectorSupplier(),
                     getMultiWindowModeStateDispatcher(), getLifecycleDispatcher(), this);
         } else {
             mMultiInstanceManager = null;
@@ -1508,7 +1508,7 @@
                 getActivityTabProvider(), mEphemeralTabCoordinatorSupplier,
                 mTabModelProfileSupplier, mBookmarkBridgeSupplier,
                 getOverviewModeBehaviorSupplier(), this::getContextualSearchManager,
-                mTabModelSelectorSupplier, mStartSurfaceSupplier,
+                getTabModelSelectorSupplier(), mStartSurfaceSupplier,
                 mLayoutStateProviderOneshotSupplier, mStartSurfaceParentTabSupplier);
     }
 
@@ -1691,7 +1691,7 @@
                     mRootUiCoordinator.getBottomSheetController(),
                     /* ChromeActivityNativeDelegate */ this, /* isCustomTab= */ false,
                     getBrowserControlsManager(), getFullscreenManager(),
-                    /* TabCreatorManager */ this, mTabModelSelectorSupplier,
+                    /* TabCreatorManager */ this, getTabModelSelectorSupplier(),
                     this::getCompositorViewHolder, getModalDialogManagerSupplier());
         }
         return mTabDelegateFactory;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index 13efedb5..ca3d1a11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -139,9 +139,6 @@
   "ExploreSitesIPH\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "ExternalNavigationDelegateImpl\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "ScreenshotTask\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index cd0ffc0..08a32a15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -152,6 +152,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorProfileSupplier;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
@@ -241,9 +242,14 @@
 
     private C mComponent;
 
-    protected ObservableSupplierImpl<TabModelSelector> mTabModelSelectorSupplier =
-            new ObservableSupplierImpl<>();
-    protected ObservableSupplier<Profile> mTabModelProfileSupplier =
+    /** Used to access the {@link ShareDelegate} from {@link WindowAndroid}. */
+    private final UnownedUserDataSupplier<ShareDelegate> mShareDelegateSupplier =
+            new ShareDelegateSupplier();
+    /** Used to access the {@link TabModelSelector} from {@link WindowAndroid}. */
+    private final UnownedUserDataSupplier<TabModelSelector> mTabModelSelectorSupplier =
+            new TabModelSelectorSupplier();
+
+    protected TabModelSelectorProfileSupplier mTabModelProfileSupplier =
             new TabModelSelectorProfileSupplier(mTabModelSelectorSupplier);
     protected ObservableSupplierImpl<BookmarkBridge> mBookmarkBridgeSupplier =
             new ObservableSupplierImpl<>();
@@ -341,11 +347,6 @@
     private List<MenuOrKeyboardActionController.MenuOrKeyboardActionHandler> mMenuActionHandlers =
             new ArrayList<>();
 
-    // UnownedUserDataSuppliers
-    /** Allows accessing {@link ShareDelegate} from classes created from native. */
-    private UnownedUserDataSupplier<ShareDelegate> mShareDelegateSupplier =
-            new ShareDelegateSupplier();
-
     protected ChromeActivity() {
         mIntentHandler = new IntentHandler(this, createIntentHandlerDelegate());
     }
@@ -358,8 +359,7 @@
 
     @Override
     public void performPreInflationStartup() {
-        // Setup UnownedUserData suppliers before they're used.
-        mShareDelegateSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
+        setupUnownedUserDataSuppliers();
 
         // Make sure the root coordinator is created prior to calling super to ensure all
         // the activity lifecycle events are called.
@@ -403,6 +403,11 @@
         getWindow().setBackgroundDrawable(getBackgroundDrawable());
     }
 
+    private void setupUnownedUserDataSuppliers() {
+        mShareDelegateSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
+        mTabModelSelectorSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
+    }
+
     protected RootUiCoordinator createRootUiCoordinator() {
         // TODO(https://crbug.com/931496): Remove dependency on ChromeActivity in favor of passing
         // in direct dependencies on needed classes. While migrating code from Chrome*Activity
@@ -411,7 +416,7 @@
         // a recommended pattern.
         return new RootUiCoordinator(this, null, getShareDelegateSupplier(),
                 getActivityTabProvider(), mTabModelProfileSupplier, mBookmarkBridgeSupplier,
-                this::getContextualSearchManager, mTabModelSelectorSupplier,
+                this::getContextualSearchManager, getTabModelSelectorSupplier(),
                 new OneshotSupplierImpl<>(), new OneshotSupplierImpl<>(),
                 new OneshotSupplierImpl<>(), () -> null);
     }
@@ -426,7 +431,7 @@
 
         ChromeActivityCommonsModule commonsModule = overridenCommonsFactory == null
                 ? new ChromeActivityCommonsModule(this,
-                        mRootUiCoordinator::getBottomSheetController, mTabModelSelectorSupplier,
+                        mRootUiCoordinator::getBottomSheetController, getTabModelSelectorSupplier(),
                         getBrowserControlsManager(), getBrowserControlsManager(),
                         getBrowserControlsManager(), getFullscreenManager(),
                         getLayoutManagerSupplier(), getLifecycleDispatcher(),
@@ -440,7 +445,7 @@
                         /* ChromeActivityNativeDelegate */ this, getModalDialogManagerSupplier(),
                         getBrowserControlsManager())
                 : overridenCommonsFactory.create(this, mRootUiCoordinator::getBottomSheetController,
-                        mTabModelSelectorSupplier, getBrowserControlsManager(),
+                        getTabModelSelectorSupplier(), getBrowserControlsManager(),
                         getBrowserControlsManager(), getBrowserControlsManager(),
                         getFullscreenManager(), getLayoutManagerSupplier(),
                         getLifecycleDispatcher(), this::getSnackbarManager, mActivityTabProvider,
@@ -1358,7 +1363,10 @@
 
         if (mShareDelegateSupplier != null) {
             mShareDelegateSupplier.destroy();
-            mShareDelegateSupplier = null;
+        }
+
+        if (mTabModelSelectorSupplier != null) {
+            mTabModelSelectorSupplier.destroy();
         }
 
         mActivityTabProvider.destroy();
@@ -1640,7 +1648,9 @@
      * {@link TabModelSelector} no longer implements TabModel.  Use getTabModelSelector() or
      * getCurrentTabModel() depending on your needs.
      * @return The {@link TabModelSelector}, possibly null.
+     * @deprecated in favor of getTabModelSelectorSupplier.
      */
+    @Deprecated
     public TabModelSelector getTabModelSelector() {
         if (!mTabModelOrchestrator.areTabModelsInitialized()) {
             throw new IllegalStateException(
@@ -1650,6 +1660,14 @@
     }
 
     /**
+     * Returns an {@link ObservableSupplier} for {@link TabModelSelector}. Prefer this method over
+     * using {@link #getTabModelSelector()} directly.
+     */
+    public final ObservableSupplier<TabModelSelector> getTabModelSelectorSupplier() {
+        return mTabModelSelectorSupplier;
+    }
+
+    /**
      * @return The provider of the visible tab in the current activity.
      */
     public ActivityTabProvider getActivityTabProvider() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
index 7c086882..f07d1cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
@@ -73,7 +73,7 @@
     // Whether to allow to refetch tab thumbnail if the aspect ratio is not matching.
     public static final BooleanCachedFieldTrialParameter ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION =
             new BooleanCachedFieldTrialParameter(
-                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, "allow_to_refetch", false);
+                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, "allow_to_refetch", true);
 
     @VisibleForTesting
     public static final String UMA_THUMBNAIL_FETCHING_RESULT =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index a3a0eb41..d976e5a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -688,6 +688,8 @@
      * @param searchUrlFull The URL for the full search to present in the overlay, or empty.
      * @param searchUrlPreload The URL for the search to preload into the overlay, or empty.
      * @param cocaCardTag The primary internal Coca card tag for the response, or {@code 0} if none.
+     * @param relatedSearches The queries known as Related Searches. These are suggested searches
+     *        related to the context.
      */
     @CalledByNative
     public void onSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
@@ -697,7 +699,7 @@
             final String caption, final String quickActionUri,
             @QuickActionCategory final int quickActionCategory, final long loggedEventId,
             final String searchUrlFull, final String searchUrlPreload,
-            @CardTag final int cocaCardTag) {
+            @CardTag final int cocaCardTag, final String[] relatedSearches) {
         ContextualSearchUma.logResolveReceived(mSelectionController.isTapSelection());
         ResolvedSearchTerm resolvedSearchTerm =
                 new ResolvedSearchTerm
@@ -705,7 +707,7 @@
                                 alternateTerm, mid, doPreventPreload, selectionStartAdjust,
                                 selectionEndAdjust, contextLanguage, thumbnailUrl, caption,
                                 quickActionUri, quickActionCategory, loggedEventId, searchUrlFull,
-                                searchUrlPreload, cocaCardTag)
+                                searchUrlPreload, cocaCardTag, relatedSearches)
                         .build();
         mNetworkCommunicator.handleSearchTermResolutionResponse(resolvedSearchTerm);
     }
@@ -738,6 +740,7 @@
                 || !TextUtils.isEmpty(resolvedSearchTerm.thumbnailUrl());
 
         assert mSearchPanel != null;
+        // TODO(donnd): Pass Related Searches into the Panel for display.
         mSearchPanel.onSearchTermResolved(message, resolvedSearchTerm.thumbnailUrl(),
                 resolvedSearchTerm.quickActionUri(), resolvedSearchTerm.quickActionCategory(),
                 resolvedSearchTerm.cardTagEnum());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ResolvedSearchTerm.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ResolvedSearchTerm.java
index f3c35330..e8494a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ResolvedSearchTerm.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ResolvedSearchTerm.java
@@ -59,6 +59,7 @@
     private final String mSearchUrlPreload;
     @CardTag
     private final int mCardTagEnum;
+    private final String mRelatedSearches[];
 
     /**
      * Called in response to the
@@ -88,6 +89,7 @@
      * @param searchUrlPreload The URL for the search to preload into the overlay, or empty.
      * @param cardTagEnum A {@link CardTag} enumeration indicating what kind of card was returned,
      *        or {@code 0} if no card was returned.
+     * @param relatedSearches An array of searches that may be related to the content.
      */
     private ResolvedSearchTerm(boolean isNetworkUnavailable, int responseCode,
             final String searchTerm, final String displayText, final String alternateTerm,
@@ -96,7 +98,7 @@
             final String caption, final String quickActionUri,
             @QuickActionCategory final int quickActionCategory, final long loggedEventId,
             final String searchUrlFull, final String searchUrlPreload,
-            @CardTag final int cardTagEnum) {
+            @CardTag final int cardTagEnum, final String[] relatedSearches) {
         mIsNetworkUnavailable = isNetworkUnavailable;
         mResponseCode = responseCode;
         mSearchTerm = searchTerm;
@@ -115,6 +117,7 @@
         mSearchUrlFull = searchUrlFull;
         mSearchUrlPreload = searchUrlPreload;
         mCardTagEnum = cardTagEnum;
+        mRelatedSearches = relatedSearches;
     }
 
     public boolean isNetworkUnavailable() {
@@ -218,6 +221,10 @@
         }
     }
 
+    public String[] relatedSearches() {
+        return mRelatedSearches;
+    }
+
     @Override
     public String toString() {
         List<String> sections = buildTextSections();
@@ -230,6 +237,8 @@
             sections.add("Network unavailable!");
         } else if (mResponseCode != HttpURLConnection.HTTP_OK) {
             sections.add("ResponseCode:" + mResponseCode);
+        } else if (relatedSearches().length > 0) {
+            sections.add("Related Searches: [" + TextUtils.join(", ", mRelatedSearches) + "]");
         } else {
             if (mDoPreventPreload) sections.add("Preventing preload!");
             if (!TextUtils.isEmpty(mSearchTerm)) sections.add("Search for '" + mSearchTerm + "'");
@@ -284,7 +293,9 @@
         private String mSearchUrlPreload;
         @CardTag
         private int mCardTagEnum;
+        private String[] mRelatedSearches;
 
+        /** Starts building using the given {@link ResolvedSearchTerm}. */
         public Builder(ResolvedSearchTerm resolvedSearchTerm) {
             mIsNetworkUnavailable = resolvedSearchTerm.mIsNetworkUnavailable;
             mResponseCode = resolvedSearchTerm.mResponseCode;
@@ -304,6 +315,7 @@
             mSearchUrlFull = resolvedSearchTerm.mSearchUrlFull;
             mSearchUrlPreload = resolvedSearchTerm.mSearchUrlPreload;
             mCardTagEnum = resolvedSearchTerm.mCardTagEnum;
+            mRelatedSearches = resolvedSearchTerm.mRelatedSearches;
         }
 
         /**
@@ -337,7 +349,7 @@
                 final String displayText, final String alternateTerm, boolean doPreventPreload) {
             this(isNetworkUnavailable, responseCode, searchTerm, displayText, alternateTerm, "",
                     doPreventPreload, 0, 0, "", "", "", "", QuickActionCategory.NONE, 0L, "", "",
-                    0);
+                    CardTag.CT_NONE, new String[0]);
         }
 
         /**
@@ -368,6 +380,7 @@
          * @param searchUrlPreload The URL for the search to preload into the overlay, or empty.
          * @param cardTag The primary internal Coca card tag for the resolution, or {@code 0} if
          *         none.
+         * @param relatedSearches An array of searches that may be related to the content.
          */
         public Builder(boolean isNetworkUnavailable, int responseCode, final String searchTerm,
                 final String displayText, final String alternateTerm, final String mid,
@@ -375,7 +388,7 @@
                 final String contextLanguage, final String thumbnailUrl, final String caption,
                 final String quickActionUri, @QuickActionCategory final int quickActionCategory,
                 final long loggedEventId, final String searchUrlFull, final String searchUrlPreload,
-                final int cardTag) {
+                @CardTag final int cardTag, final String[] relatedSearches) {
             mIsNetworkUnavailable = isNetworkUnavailable;
             mResponseCode = responseCode;
             mSearchTerm = searchTerm;
@@ -394,6 +407,7 @@
             mSearchUrlFull = searchUrlFull;
             mSearchUrlPreload = searchUrlPreload;
             mCardTagEnum = fromCocaCardTag(cardTag);
+            mRelatedSearches = relatedSearches;
         }
 
         /**
@@ -522,6 +536,15 @@
         }
 
         /**
+         * @param relatedSearches The list of related searches generated from the context.
+.
+         */
+        public Builder setRelatedSearches(String[] relatedSearches) {
+            mRelatedSearches = relatedSearches;
+            return this;
+        }
+
+        /**
          * Builds the {@link ResolvedSearchTerm} based on the params passed into the constructor
          * of this builder, plus whatever settings have been established.
          * @return The {@link ResolvedSearchTerm}, which represents all the results sent back by
@@ -532,7 +555,7 @@
                     mDisplayText, mAlternateTerm, mMid, mDoPreventPreload, mSelectionStartAdjust,
                     mSelectionEndAdjust, mContextLanguage, mThumbnailUrl, mCaption, mQuickActionUri,
                     mQuickActionCategory, mLoggedEventId, mSearchUrlFull, mSearchUrlPreload,
-                    mCardTagEnum);
+                    mCardTagEnum, mRelatedSearches);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/SimpleSearchTermResolver.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/SimpleSearchTermResolver.java
index 7df18f7..d0e4f6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/SimpleSearchTermResolver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/SimpleSearchTermResolver.java
@@ -99,6 +99,8 @@
      * @param searchUrlFull The URL for the full search to present in the overlay, or empty.
      * @param searchUrlPreload The URL for the search to preload into the overlay, or empty.
      * @param cocaCardTag The primary internal Coca card tag for the response, or {@code 0} if none.
+     * @param relatedSearches The query suggestions known as Related Searches returned from the
+     *        Claire backend through GoC. These are suggested searches based on the URL or content.
      */
     @CalledByNative
     public void onSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
@@ -108,14 +110,14 @@
             final String caption, final String quickActionUri,
             @QuickActionCategory final int quickActionCategory, final long loggedEventId,
             final String searchUrlFull, final String searchUrlPreload,
-            @CardTag final int cocaCardTag) {
+            @CardTag final int cocaCardTag, final String[] relatedSearches) {
         ResolvedSearchTerm resolvedSearchTerm =
                 new ResolvedSearchTerm
                         .Builder(isNetworkUnavailable, responseCode, searchTerm, displayText,
                                 alternateTerm, mid, doPreventPreload, selectionStartAdjust,
                                 selectionEndAdjust, contextLanguage, thumbnailUrl, caption,
                                 quickActionUri, quickActionCategory, loggedEventId, searchUrlFull,
-                                searchUrlPreload, cocaCardTag)
+                                searchUrlPreload, cocaCardTag, relatedSearches)
                         .build();
         Log.v(TAG, "onSearchTermResolutionResponse received with " + resolvedSearchTerm);
         if (!TextUtils.isEmpty(resolvedSearchTerm.searchTerm())) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 0f6c4ee..d97e2630 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -147,7 +147,7 @@
                 ()
                         -> mNavigationController,
                 getActivityTabProvider(), mTabModelProfileSupplier, mBookmarkBridgeSupplier,
-                this::getContextualSearchManager, mTabModelSelectorSupplier);
+                this::getContextualSearchManager, getTabModelSelectorSupplier());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index e9944f07..633b674 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -20,10 +20,10 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.PackageManagerUtils;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ChromeTabbedActivity2;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity;
@@ -33,6 +33,8 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabUtils;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
 import org.chromium.components.external_intents.ExternalNavigationDelegate;
 import org.chromium.components.external_intents.ExternalNavigationDelegate.StartActivityIfNeededResult;
 import org.chromium.components.external_intents.ExternalNavigationHandler;
@@ -53,10 +55,13 @@
     protected final Context mApplicationContext;
     private final Tab mTab;
     private final TabObserver mTabObserver;
+    private final Supplier<TabModelSelector> mTabModelSelectorSupplier;
+
     private boolean mIsTabDestroyed;
 
     public ExternalNavigationDelegateImpl(Tab tab) {
         mTab = tab;
+        mTabModelSelectorSupplier = TabModelSelectorSupplier.from(tab.getWindowAndroid());
         mApplicationContext = ContextUtils.getApplicationContext();
         mTabObserver = new EmptyTabObserver() {
             @Override
@@ -202,10 +207,8 @@
     @Override
     public void closeTab() {
         if (!hasValidTab()) return;
-        Context context = mTab.getWindowAndroid().getContext().get();
-        if (context instanceof ChromeActivity) {
-            ((ChromeActivity) context).getTabModelSelector().closeTab(mTab);
-        }
+        if (!mTabModelSelectorSupplier.hasValue()) return;
+        mTabModelSelectorSupplier.get().closeTab(mTab);
     }
 
     @Override
@@ -299,8 +302,8 @@
      */
     protected void startAutofillAssistantWithIntent(
             Intent targetIntent, String browserFallbackUrl) {
-        AutofillAssistantFacade.start((ChromeActivity) TabUtils.getActivity(mTab),
-                targetIntent.getExtras(), browserFallbackUrl);
+        AutofillAssistantFacade.start(
+                TabUtils.getActivity(mTab), targetIntent.getExtras(), browserFallbackUrl);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index aad9a873..d67011ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -15,7 +15,6 @@
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.LocaleUtils;
 import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
 import org.chromium.base.ThreadUtils;
@@ -28,7 +27,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.AppHooks;
-import org.chromium.chrome.browser.ChromeLocalizationUtils;
 import org.chromium.chrome.browser.ChromeStrictMode;
 import org.chromium.chrome.browser.FileProviderHelper;
 import org.chromium.chrome.browser.app.flags.ChromeCachedFlags;
@@ -47,7 +45,6 @@
 import org.chromium.content_public.browser.SpeechRecognition;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.net.NetworkChangeNotifier;
-import org.chromium.ui.resources.ResourceExtractor;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -171,7 +168,7 @@
      */
     private void onInflationComplete(final BrowserParts parts) {
         if (parts.isActivityFinishingOrDestroyed()) return;
-        postInflationStartup();
+        mPostInflationStartupComplete = true;
         parts.postInflationStartup();
     }
 
@@ -225,20 +222,6 @@
         mPreInflationStartupComplete = true;
     }
 
-    private void postInflationStartup() {
-        ThreadUtils.assertOnUiThread();
-        if (mPostInflationStartupComplete) return;
-
-        // Check to see if we need to extract any new resources from the APK. This could
-        // be on first run when we need to extract all the .pak files we need, or after
-        // the user has switched locale, in which case we want new locale resources.
-        ResourceExtractor.get().setResultTraits(UiThreadTaskTraits.BOOTSTRAP);
-        ResourceExtractor.get().startExtractingResources(
-                LocaleUtils.toLanguage(ChromeLocalizationUtils.getJavaUiLocale()));
-
-        mPostInflationStartupComplete = true;
-    }
-
     /**
      * Execute startup tasks that require native libraries to be loaded. See {@link BrowserParts}
      * for a list of calls to be implemented.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
index c33dd7d..0b4a7c4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
@@ -4,12 +4,14 @@
 
 package org.chromium.chrome.browser.notifications;
 
+import android.app.Activity;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
+import android.os.Bundle;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -57,42 +59,59 @@
     }
 
     /**
-     * Receives the event when the user taps on the notification body, notification action, or
-     * dismiss notification.
+     * Deprecated, now we use {@link TrampolineActivity} to do the logging. Temporarily kept or
+     * existing notification will crash. Receives the event when the user taps on the notification
+     * body, notification action, or dismiss notification.
      * {@link Notification#contentIntent}, {@link Notification#deleteIntent}
      * {@link Notification.Action#actionIntent} will be delivered to this broadcast receiver.
      */
     public static final class Receiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            @IntentType
-            int intentType = intent.getIntExtra(EXTRA_INTENT_TYPE, IntentType.UNKNOWN);
-            @NotificationUmaTracker.SystemNotificationType
-            int notificationType = intent.getIntExtra(
-                    EXTRA_NOTIFICATION_TYPE, NotificationUmaTracker.SystemNotificationType.UNKNOWN);
+            processIntent(context, intent);
+        }
+    }
 
-            long createTime = intent.getLongExtra(EXTRA_CREATE_TIME, INVALID_CREATE_TIME);
+    private static void processIntent(Context context, Intent intent) {
+        @IntentType
+        int intentType = intent.getIntExtra(EXTRA_INTENT_TYPE, IntentType.UNKNOWN);
+        @NotificationUmaTracker.SystemNotificationType
+        int notificationType = intent.getIntExtra(
+                EXTRA_NOTIFICATION_TYPE, NotificationUmaTracker.SystemNotificationType.UNKNOWN);
 
-            switch (intentType) {
-                case IntentType.UNKNOWN:
-                    break;
-                case IntentType.CONTENT_INTENT:
-                    NotificationUmaTracker.getInstance().onNotificationContentClick(
-                            notificationType, createTime);
-                    break;
-                case IntentType.DELETE_INTENT:
-                    NotificationUmaTracker.getInstance().onNotificationDismiss(
-                            notificationType, createTime);
-                    break;
-                case IntentType.ACTION_INTENT:
-                    int actionType = intent.getIntExtra(
-                            EXTRA_ACTION_TYPE, NotificationUmaTracker.ActionType.UNKNOWN);
-                    NotificationUmaTracker.getInstance().onNotificationActionClick(
-                            actionType, notificationType, createTime);
-                    break;
-            }
+        long createTime = intent.getLongExtra(EXTRA_CREATE_TIME, INVALID_CREATE_TIME);
 
-            forwardPendingIntent(intent);
+        switch (intentType) {
+            case IntentType.UNKNOWN:
+                break;
+            case IntentType.CONTENT_INTENT:
+                NotificationUmaTracker.getInstance().onNotificationContentClick(
+                        notificationType, createTime);
+                break;
+            case IntentType.DELETE_INTENT:
+                NotificationUmaTracker.getInstance().onNotificationDismiss(
+                        notificationType, createTime);
+                break;
+            case IntentType.ACTION_INTENT:
+                int actionType = intent.getIntExtra(
+                        EXTRA_ACTION_TYPE, NotificationUmaTracker.ActionType.UNKNOWN);
+                NotificationUmaTracker.getInstance().onNotificationActionClick(
+                        actionType, notificationType, createTime);
+                break;
+        }
+
+        forwardPendingIntent(intent);
+    }
+
+    /**
+     * A trampoline activity that handles notification events logging.
+     */
+    public static class TrampolineActivity extends Activity {
+        @Override
+        protected void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            processIntent(getApplicationContext(), getIntent());
+            finish();
         }
     }
 
@@ -118,7 +137,8 @@
             flags = pendingIntentProvider.getFlags();
         }
         Context applicationContext = ContextUtils.getApplicationContext();
-        Intent intent = new Intent(applicationContext, Receiver.class);
+        Intent intent = new Intent(applicationContext, TrampolineActivity.class);
+
         intent.setAction(INTENT_ACTION);
         intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
         intent.putExtra(EXTRA_INTENT_TYPE, intentType);
@@ -137,7 +157,8 @@
         int originalRequestCode =
                 pendingIntentProvider != null ? pendingIntentProvider.getRequestCode() : 0;
         int requestCode = computeHashCode(metadata, intentType, intentId, originalRequestCode);
-        return PendingIntent.getBroadcast(applicationContext, requestCode, intent, flags);
+
+        return PendingIntent.getActivity(applicationContext, requestCode, intent, flags);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
index 62489c34..d535b38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -115,7 +115,8 @@
         for (Tile tile : sectionTiles) {
             SuggestionsTileView tileView = oldTileViews.get(tile.getData());
             if (tileView == null || tileView.mIconView == null
-                    || tileView.mIconView.getDrawable() == null) {
+                    || tileView.mIconView.getDrawable() == null
+                    || tile.getSource() == TileSource.EXPLORE) {
                 tileView = buildTileView(tile, parent, setupDelegate);
             }
 
@@ -155,19 +156,16 @@
                     VectorDrawableCompat.create(mResources, R.drawable.ic_apps_blue_24dp, mTheme));
             tile.setType(TileVisualType.ICON_DEFAULT);
 
-            if (!LibraryLoader.getInstance().isInitialized()) {
-                tileView.initialize(tile, mTitleLinesCount);
-                return tileView;
+            if (LibraryLoader.getInstance().isInitialized()) {
+                // One task to load actual icon.
+                LargeIconBridge.LargeIconCallback bridgeCallback =
+                        setupDelegate.createIconLoadCallback(tile);
+                ExploreSitesBridge.getSummaryImage(Profile.getLastUsedRegularProfile(),
+                        mDesiredIconSize,
+                        (Bitmap img)
+                                -> bridgeCallback.onLargeIconAvailable(
+                                        img, Color.BLACK, false, IconType.FAVICON));
             }
-
-            // One task to load actual icon.
-            LargeIconBridge.LargeIconCallback bridgeCallback =
-                    setupDelegate.createIconLoadCallback(tile);
-            ExploreSitesBridge.getSummaryImage(Profile.getLastUsedRegularProfile(),
-                    mDesiredIconSize,
-                    (Bitmap img)
-                            -> bridgeCallback.onLargeIconAvailable(
-                                    img, Color.BLACK, false, IconType.FAVICON));
         } else {
             tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext())
                                .inflate(mLayout, parentView, false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
index e7265bc..3ed9096 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
@@ -188,10 +188,10 @@
     private boolean isNewTabVariationEnabled() {
         return mIsGridTabSwitcherEnabled && ChromeFeatureList.isInitialized()
                 && IncognitoUtils.isIncognitoModeEnabled()
-                && ChromeFeatureList
-                           .getFieldTrialParamByFeature(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
-                                   "tab_grid_layout_android_new_tab")
-                           .equals("NewTabVariation");
+                && !ChromeFeatureList
+                            .getFieldTrialParamByFeature(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
+                                    "tab_grid_layout_android_new_tab")
+                            .equals("false");
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 2ee104f..2c61912 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -2465,9 +2465,13 @@
     @Feature({"ContextualSearch"})
     @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testExternalNavigationWithUserGesture(@EnabledFeature int enabledFeature) {
+        final ExternalNavigationDelegateImpl delegate =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        ()
+                                -> new ExternalNavigationDelegateImpl(
+                                        sActivityTestRule.getActivity().getActivityTab()));
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
-                        sActivityTestRule.getActivity().getActivityTab()));
+                new ExternalNavigationHandler(delegate);
         final NavigationParams navigationParams = new NavigationParams(
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 0 /* navigationId */, false /* isPost */, true /* hasUserGesture */,
@@ -2495,9 +2499,13 @@
     @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testRedirectedExternalNavigationWithUserGesture(
             @EnabledFeature int enabledFeature) {
+        final ExternalNavigationDelegateImpl delegate =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        ()
+                                -> new ExternalNavigationDelegateImpl(
+                                        sActivityTestRule.getActivity().getActivityTab()));
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
-                        sActivityTestRule.getActivity().getActivityTab()));
+                new ExternalNavigationHandler(delegate);
 
         final NavigationParams initialNavigationParams = new NavigationParams("http://test.com", "",
                 0 /* navigationId */, false /* isPost */, true /* hasUserGesture */,
@@ -2534,9 +2542,13 @@
     @Feature({"ContextualSearch"})
     @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testExternalNavigationWithoutUserGesture(@EnabledFeature int enabledFeature) {
+        final ExternalNavigationDelegateImpl delegate =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        ()
+                                -> new ExternalNavigationDelegateImpl(
+                                        sActivityTestRule.getActivity().getActivityTab()));
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
-                        sActivityTestRule.getActivity().getActivityTab()));
+                new ExternalNavigationHandler(delegate);
         final NavigationParams navigationParams = new NavigationParams(
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 0 /* navigationId */, false /* isPost */, false /* hasUserGesture */,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
index b61ee37..ff39f74b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
@@ -129,7 +129,7 @@
         public void startSearchTermResolutionRequest(String selection, boolean isExactResolve) {
             // Skip native calls and immediately "resolve" the search term.
             onSearchTermResolutionResponse(true, 200, selection, selection, "", "", false, 0, 10,
-                    "", "", "", "", QuickActionCategory.NONE, 0, "", "", 0);
+                    "", "", "", "", QuickActionCategory.NONE, 0, "", "", 0, new String[0]);
         }
 
         /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
index d51b8ff1..fb44936 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
@@ -20,11 +20,13 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.external_intents.ExternalNavigationHandler;
 import org.chromium.components.external_intents.ExternalNavigationParams;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.url.Origin;
 
 /**
@@ -48,8 +50,8 @@
     private static final boolean IS_GOOGLE_REFERRER = true;
 
     class ExternalNavigationDelegateImplForTesting extends ExternalNavigationDelegateImpl {
-        public ExternalNavigationDelegateImplForTesting() {
-            super(mActivityTestRule.getActivity().getActivityTab());
+        public ExternalNavigationDelegateImplForTesting(Tab activityTab) {
+            super(activityTab);
         }
 
         @Override
@@ -99,22 +101,29 @@
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
+    private ExternalNavigationDelegateImpl mExternalNavigationDelegateImpl;
+    private ExternalNavigationDelegateImplForTesting mExternalNavigationDelegateImplForTesting;
+
     @Before
     public void setUp() throws InterruptedException {
         mActivityTestRule.startMainActivityOnBlankPage();
+        Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mActivityTestRule.getActivity().getActivityTab());
+        mExternalNavigationDelegateImpl = TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> new ExternalNavigationDelegateImpl(tab));
+        mExternalNavigationDelegateImplForTesting =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        () -> new ExternalNavigationDelegateImplForTesting(tab));
     }
 
     @Test
     @SmallTest
     public void testMaybeSetPendingIncognitoUrl() {
-        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
-                mActivityTestRule.getActivity().getActivityTab());
-
         String url = "http://www.example.com";
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(url));
 
-        delegate.maybeSetPendingIncognitoUrl(intent);
+        mExternalNavigationDelegateImpl.maybeSetPendingIncognitoUrl(intent);
 
         Assert.assertTrue(
                 intent.getBooleanExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, false));
@@ -124,9 +133,6 @@
     @Test
     @SmallTest
     public void testIsIntentToInstantApp() {
-        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
-                mActivityTestRule.getActivity().getActivityTab());
-
         // Check that the delegate correctly distinguishes instant app intents from others.
         String vanillaUrl = "http://www.example.com";
         Intent vanillaIntent = new Intent(Intent.ACTION_VIEW);
@@ -147,8 +153,8 @@
             return;
         }
 
-        Assert.assertFalse(delegate.isIntentToInstantApp(vanillaIntent));
-        Assert.assertTrue(delegate.isIntentToInstantApp(instantAppIntent));
+        Assert.assertFalse(mExternalNavigationDelegateImpl.isIntentToInstantApp(vanillaIntent));
+        Assert.assertTrue(mExternalNavigationDelegateImpl.isIntentToInstantApp(instantAppIntent));
 
         // Check that Supervisor is detected by action even without package.
         for (String action : SUPERVISOR_START_ACTIONS) {
@@ -165,59 +171,54 @@
                 Assert.assertTrue(false);
                 return;
             }
-            Assert.assertTrue(delegate.isIntentToInstantApp(instantAppIntent));
+            Assert.assertTrue(
+                    mExternalNavigationDelegateImpl.isIntentToInstantApp(instantAppIntent));
         }
     }
 
     @Test
     @SmallTest
     public void testMaybeAdjustInstantAppExtras() {
-        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
-                mActivityTestRule.getActivity().getActivityTab());
-
         String url = "http://www.example.com";
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(url));
 
-        delegate.maybeAdjustInstantAppExtras(intent, /*isIntentToInstantApp=*/true);
+        mExternalNavigationDelegateImpl.maybeAdjustInstantAppExtras(
+                intent, /*isIntentToInstantApp=*/true);
         Assert.assertTrue(intent.hasExtra(InstantAppsHandler.IS_GOOGLE_SEARCH_REFERRER));
 
-        delegate.maybeAdjustInstantAppExtras(intent, /*isIntentToInstantApp=*/false);
+        mExternalNavigationDelegateImpl.maybeAdjustInstantAppExtras(
+                intent, /*isIntentToInstantApp=*/false);
         Assert.assertFalse(intent.hasExtra(InstantAppsHandler.IS_GOOGLE_SEARCH_REFERRER));
     }
 
     @Test
     @SmallTest
     public void maybeSetRequestMetadata() {
-        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
-                mActivityTestRule.getActivity().getActivityTab());
-
         String url = "http://www.example.com";
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(url));
 
-        delegate.maybeSetRequestMetadata(intent, false, false, null);
+        mExternalNavigationDelegateImpl.maybeSetRequestMetadata(intent, false, false, null);
         Assert.assertNull(
                 IntentWithRequestMetadataHandler.getInstance().getRequestMetadataAndClear(intent));
 
-        maybeSetAndGetRequestMetadata(delegate, intent, true, true, null);
-        maybeSetAndGetRequestMetadata(delegate, intent, true, false, null);
-        maybeSetAndGetRequestMetadata(delegate, intent, false, true, null);
-        maybeSetAndGetRequestMetadata(delegate, intent, false, false, new MockOrigin());
+        maybeSetAndGetRequestMetadata(mExternalNavigationDelegateImpl, intent, true, true, null);
+        maybeSetAndGetRequestMetadata(mExternalNavigationDelegateImpl, intent, true, false, null);
+        maybeSetAndGetRequestMetadata(mExternalNavigationDelegateImpl, intent, false, true, null);
+        maybeSetAndGetRequestMetadata(
+                mExternalNavigationDelegateImpl, intent, false, false, new MockOrigin());
     }
 
     @Test
     @SmallTest
     public void testMaybeSetPendingReferrer() {
-        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
-                mActivityTestRule.getActivity().getActivityTab());
-
         String url = "http://www.example.com";
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(url));
 
         String referrerUrl = "http://www.example-referrer.com";
-        delegate.maybeSetPendingReferrer(intent, referrerUrl);
+        mExternalNavigationDelegateImpl.maybeSetPendingReferrer(intent, referrerUrl);
 
         Assert.assertEquals(
                 Uri.parse(referrerUrl), intent.getParcelableExtra(Intent.EXTRA_REFERRER));
@@ -231,16 +232,14 @@
             ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
     public void
     testHandleWithAutofillAssistant_TriggersFromSearch() {
-        ExternalNavigationDelegateImplForTesting delegate =
-                new ExternalNavigationDelegateImplForTesting();
-
         ExternalNavigationParams params =
                 new ExternalNavigationParams
                         .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
                         .build();
 
-        Assert.assertTrue(delegate.handleWithAutofillAssistant(params, IS_GOOGLE_REFERRER));
-        Assert.assertTrue(delegate.wasAutofillAssistantStarted());
+        Assert.assertTrue(mExternalNavigationDelegateImplForTesting.handleWithAutofillAssistant(
+                params, IS_GOOGLE_REFERRER));
+        Assert.assertTrue(mExternalNavigationDelegateImplForTesting.wasAutofillAssistantStarted());
     }
 
     @Test
@@ -249,16 +248,14 @@
             ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
     public void
     testHandleWithAutofillAssistant_DoesNotTriggerFromSearchInIncognito() {
-        ExternalNavigationDelegateImplForTesting delegate =
-                new ExternalNavigationDelegateImplForTesting();
-
         ExternalNavigationParams params =
                 new ExternalNavigationParams
                         .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/true)
                         .build();
 
-        Assert.assertFalse(delegate.handleWithAutofillAssistant(params, IS_GOOGLE_REFERRER));
-        Assert.assertFalse(delegate.wasAutofillAssistantStarted());
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.handleWithAutofillAssistant(
+                params, IS_GOOGLE_REFERRER));
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.wasAutofillAssistantStarted());
     }
 
     @Test
@@ -267,16 +264,14 @@
             ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
     public void
     testHandleWithAutofillAssistant_DoesNotTriggerFromDifferentOrigin() {
-        ExternalNavigationDelegateImplForTesting delegate =
-                new ExternalNavigationDelegateImplForTesting();
-
         ExternalNavigationParams params =
                 new ExternalNavigationParams
                         .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
                         .build();
 
-        Assert.assertFalse(delegate.handleWithAutofillAssistant(params, !IS_GOOGLE_REFERRER));
-        Assert.assertFalse(delegate.wasAutofillAssistantStarted());
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.handleWithAutofillAssistant(
+                params, !IS_GOOGLE_REFERRER));
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.wasAutofillAssistantStarted());
     }
 
     @Test
@@ -285,15 +280,13 @@
             ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
     public void
     testHandleWithAutofillAssistant_DoesNotTriggerWhenFeatureDisabled() {
-        ExternalNavigationDelegateImplForTesting delegate =
-                new ExternalNavigationDelegateImplForTesting();
-
         ExternalNavigationParams params =
                 new ExternalNavigationParams
                         .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
                         .build();
 
-        Assert.assertFalse(delegate.handleWithAutofillAssistant(params, IS_GOOGLE_REFERRER));
-        Assert.assertFalse(delegate.wasAutofillAssistantStarted());
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.handleWithAutofillAssistant(
+                params, IS_GOOGLE_REFERRER));
+        Assert.assertFalse(mExternalNavigationDelegateImplForTesting.wasAutofillAssistantStarted());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index beeb149..3c2bff24 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions;
 
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -19,7 +18,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.SparseArray;
 import android.view.View;
 
@@ -32,16 +33,13 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
@@ -67,6 +65,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Tests for {@link AutocompleteMediator}.
@@ -78,8 +77,10 @@
     private static final int SUGGESTION_MIN_HEIGHT = 20;
     private static final int HEADER_MIN_HEIGHT = 15;
 
-    // Empty AutocompleteDelegate implementation for test. This is to work around an issue that
-    // mock doesn't work on inherited methods in some builds.
+    /**
+     * Empty AutocompleteDelegate implementation for test. This is to work around an issue that
+     * mock doesn't work on inherited methods in some builds.
+     */
     static class AutocompleteDelegateForTest implements AutocompleteDelegate {
         @Override
         public void setOmniboxEditingText(String text) {}
@@ -124,6 +125,42 @@
         }
     }
 
+    /**
+     * A variant of Handler, that schedules all delayed tasks for an immediate execution.
+     * The handler does not invoke the calls right away; instead, the test method should call
+     * `runQueuedTasks()` to flush the queue.
+     */
+    static class ImmediatePostingHandler extends Handler {
+        ImmediatePostingHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        /**
+         * Schedule message for execution.
+         * Unlike normal loopers, this will place a supplied Message for immediate execution
+         * removing the need to wait for an arbitrary amount of time until all the delayed actions
+         * complete.
+         *
+         * @param msg Message to be scheduled.
+         * @param uptimeMillis The absolute time at which the message should be delivered. This
+         *         parameter is ignored and replaced with current time.
+         */
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            return super.sendMessageAtTime(msg, SystemClock.uptimeMillis());
+        }
+
+        /**
+         * Executes all tasks posted on this Handler and returns.
+         * Must be called from UI Thread.
+         */
+        public void runQueuedTasks() {
+            AtomicBoolean ranQueuedTasks = new AtomicBoolean(false);
+            post(() -> ranQueuedTasks.set(true));
+            CriteriaHelper.pollUiThreadNested(() -> ranQueuedTasks.get(), 100, 0);
+        }
+    }
+
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
 
@@ -152,8 +189,9 @@
     ModalDialogManager mModalDialogManager;
 
     @Mock
-    Handler mHandler;
+    Profile mProfile;
 
+    private ImmediatePostingHandler mHandler;
     private PropertyModel mListModel;
     private AutocompleteMediator mMediator;
     private List<AutocompleteMatch> mSuggestionsList;
@@ -164,6 +202,8 @@
         MockitoAnnotations.initMocks(this);
         NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
 
+        mHandler = new ImmediatePostingHandler();
+
         mSuggestionModels = new ModelList();
         mListModel = new PropertyModel(SuggestionListProperties.ALL_KEYS);
         mListModel.set(SuggestionListProperties.SUGGESTION_MODELS, mSuggestionModels);
@@ -192,14 +232,6 @@
         doReturn(HEADER_MIN_HEIGHT).when(mMockHeaderProcessor).getMinimumViewHeight();
         doReturn(OmniboxSuggestionUiType.HEADER).when(mMockHeaderProcessor).getViewTypeId();
 
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                ((Message) invocation.getArguments()[0]).getCallback().run();
-                return null;
-            }
-        }).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
-
         mSuggestionsList = buildDummySuggestionsList(10, "Suggestion");
         doReturn(true).when(mAutocompleteDelegate).isKeyboardActive();
     }
@@ -240,6 +272,21 @@
         return headers;
     }
 
+    /**
+     * Set up LocationBarDataProvider to report supplied values.
+     *
+     * @param url The URL to report as a current URL.
+     * @param title The Page Title to report.
+     * @param pageClassification The Page classification to report.
+     */
+    void setUpLocationBarDataProvider(String url, String title, int pageClassification) {
+        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
+        when(mLocationBarDataProvider.getProfile()).thenReturn(mProfile);
+        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
+        when(mLocationBarDataProvider.getTitle()).thenReturn(title);
+        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+    }
+
     @Test
     @SmallTest
     @UiThreadTest
@@ -398,22 +445,17 @@
         when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
         when(mAutocompleteDelegate.didFocusUrlFromFakebox()).thenReturn(false);
 
-        Profile profile = Mockito.mock(Profile.class);
         String url = "http://www.example.com";
         String title = "Title";
         int pageClassification = PageClassification.BLANK_VALUE;
-        when(mLocationBarDataProvider.getProfile()).thenReturn(profile);
-        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
-        when(mLocationBarDataProvider.getTitle()).thenReturn(title);
-        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
-        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+        setUpLocationBarDataProvider(url, title, pageClassification);
 
         when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
 
         mMediator.onNativeInitialized();
         mMediator.onTextChanged("", "");
         verify(mAutocompleteController)
-                .startZeroSuggest(profile, "", url, pageClassification, title);
+                .startZeroSuggest(mProfile, "", url, pageClassification, title);
     }
 
     @Test
@@ -421,13 +463,9 @@
     @UiThreadTest
     @DisableFeatures(ChromeFeatureList.OMNIBOX_ADAPTIVE_SUGGESTIONS_COUNT)
     public void onTextChanged_nonEmptyTextTriggersSuggestions() {
-        Profile profile = Mockito.mock(Profile.class);
         String url = "http://www.example.com";
         int pageClassification = PageClassification.BLANK_VALUE;
-        when(mLocationBarDataProvider.getProfile()).thenReturn(profile);
-        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
-        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
-        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+        setUpLocationBarDataProvider(url, url, pageClassification);
 
         when(mTextStateProvider.shouldAutocomplete()).thenReturn(true);
         when(mTextStateProvider.getSelectionStart()).thenReturn(4);
@@ -435,23 +473,19 @@
 
         mMediator.onNativeInitialized();
         mMediator.onTextChanged("test", "testing");
+        mHandler.runQueuedTasks();
         verify(mAutocompleteController)
-                .start(profile, url, pageClassification, "test", 4, false, null, false);
+                .start(mProfile, url, pageClassification, "test", 4, false, null, false);
     }
 
-    @DisabledTest(message = "Does not test what it says: http://crbug.com/1147443")
     @Test
     @SmallTest
     @UiThreadTest
     @DisableFeatures(ChromeFeatureList.OMNIBOX_ADAPTIVE_SUGGESTIONS_COUNT)
     public void onTextChanged_cancelsPendingRequests() {
-        Profile profile = Mockito.mock(Profile.class);
         String url = "http://www.example.com";
         int pageClassification = PageClassification.BLANK_VALUE;
-        when(mLocationBarDataProvider.getProfile()).thenReturn(profile);
-        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
-        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
-        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+        setUpLocationBarDataProvider(url, url, pageClassification);
 
         when(mTextStateProvider.shouldAutocomplete()).thenReturn(true);
         when(mTextStateProvider.getSelectionStart()).thenReturn(4);
@@ -460,8 +494,11 @@
         mMediator.onNativeInitialized();
         mMediator.onTextChanged("test", "testing");
         mMediator.onTextChanged("nottest", "nottesting");
-        verify(mAutocompleteController)
-                .start(profile, url, pageClassification, "nottest", 4, false, null, false);
+        mHandler.runQueuedTasks();
+        verify(mAutocompleteController, times(1))
+                .start(mProfile, url, pageClassification, "nottest", 4, false, null, false);
+        verify(mAutocompleteController, times(1))
+                .start(any(), any(), anyInt(), any(), anyInt(), anyBoolean(), any(), anyBoolean());
     }
 
     @Test
@@ -533,22 +570,18 @@
         when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
         when(mAutocompleteDelegate.didFocusUrlFromFakebox()).thenReturn(false);
 
-        Profile profile = Mockito.mock(Profile.class);
         String url = "http://www.example.com";
         String title = "Title";
         int pageClassification = PageClassification.BLANK_VALUE;
-        when(mLocationBarDataProvider.getProfile()).thenReturn(profile);
-        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
-        when(mLocationBarDataProvider.getTitle()).thenReturn(title);
-        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
-        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+        setUpLocationBarDataProvider(url, title, pageClassification);
 
         when(mTextStateProvider.getTextWithAutocomplete()).thenReturn(url);
 
         mMediator.onNativeInitialized();
         mMediator.onUrlFocusChange(true);
+        mHandler.runQueuedTasks();
         verify(mAutocompleteController)
-                .startZeroSuggest(profile, url, url, pageClassification, title);
+                .startZeroSuggest(mProfile, url, url, pageClassification, title);
     }
 
     @Test
@@ -559,25 +592,24 @@
         when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
         when(mAutocompleteDelegate.didFocusUrlFromFakebox()).thenReturn(false);
 
-        Profile profile = Mockito.mock(Profile.class);
         String url = "http://www.example.com";
         String title = "Title";
         int pageClassification = PageClassification.BLANK_VALUE;
-        when(mLocationBarDataProvider.getProfile()).thenReturn(profile);
-        when(mLocationBarDataProvider.getCurrentUrl()).thenReturn(url);
-        when(mLocationBarDataProvider.getTitle()).thenReturn(title);
-        when(mLocationBarDataProvider.hasTab()).thenReturn(true);
-        when(mLocationBarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+        setUpLocationBarDataProvider(url, title, pageClassification);
 
         when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
 
-        // Signal focus prior to initializing native.
+        // Signal focus prior to initializing native; confirm that zero suggest is not triggered.
         mMediator.onUrlFocusChange(true);
+        mHandler.runQueuedTasks();
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), any(), anyInt(), any());
 
         // Initialize native and ensure zero suggest is triggered.
         mMediator.onNativeInitialized();
+        mHandler.runQueuedTasks();
         verify(mAutocompleteController)
-                .startZeroSuggest(profile, "", url, pageClassification, title);
+                .startZeroSuggest(mProfile, "", url, pageClassification, title);
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
index e90fc04f..1a7eb58 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
@@ -303,8 +303,8 @@
     @MediumTest
     public void testAccountDisappearedOnCollapsedSheet() {
         buildAndShowCollapsedBottomSheet();
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA1.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA2.getAccountEmail());
         CriteriaHelper.pollUiThread(() -> {
             return !mCoordinator.getBottomSheetViewForTesting()
                             .findViewById(R.id.account_picker_selected_account)
@@ -317,8 +317,8 @@
     @MediumTest
     public void testAccountDisappearedOnExpandedSheet() {
         buildAndShowExpandedBottomSheet();
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA1.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA2.getAccountEmail());
         CriteriaHelper.pollUiThread(() -> {
             return !mCoordinator.getBottomSheetViewForTesting()
                             .findViewById(R.id.account_picker_account_list)
@@ -330,8 +330,8 @@
     @Test
     @MediumTest
     public void testAccountReappearedOnCollapsedSheet() {
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA1.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA2.getAccountEmail());
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mCoordinator = new AccountPickerBottomSheetCoordinator(sActivityTestRule.getActivity(),
                     getBottomSheetController(), mAccountPickerDelegateMock,
@@ -348,7 +348,7 @@
     public void testOtherAccountsChangeOnCollapsedSheet() {
         buildAndShowCollapsedBottomSheet();
         checkCollapsedAccountList(PROFILE_DATA1);
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA2.getAccountEmail());
         checkCollapsedAccountList(PROFILE_DATA1);
     }
 
@@ -356,7 +356,7 @@
     @MediumTest
     public void testSelectedAccountChangeOnCollapsedSheet() {
         buildAndShowCollapsedBottomSheet();
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
+        mAccountManagerTestRule.removeAccount(PROFILE_DATA1.getAccountEmail());
         checkCollapsedAccountList(PROFILE_DATA2);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
index 126b687..b65c7160 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
@@ -6,8 +6,6 @@
 
 import static org.robolectric.Shadows.shadowOf;
 
-import static org.chromium.chrome.browser.notifications.NotificationIntentInterceptor.INTENT_ACTION;
-
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -24,6 +22,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLog;
 import org.robolectric.shadows.ShadowNotificationManager;
@@ -86,8 +85,6 @@
         ContextUtils.initApplicationContextForTests(mContext);
         mShadowNotificationManager = shadowOf(
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE));
-        mContext.registerReceiver(
-                new NotificationIntentInterceptor.Receiver(), new IntentFilter(INTENT_ACTION));
         mReceiver = new TestReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TestReceiver.TEST_ACTION));
     }
@@ -131,6 +128,15 @@
         return builder.buildNotificationWrapper();
     }
 
+    private void sendPendingIntent(PendingIntent pendingIntent) {
+        // Simulate to send a PendingIntent by manually starting the TrampolineActivity.
+        ShadowPendingIntent shadowPendingIntent = Shadows.shadowOf(pendingIntent);
+        Robolectric
+                .buildActivity(NotificationIntentInterceptor.TrampolineActivity.class,
+                        shadowPendingIntent.getSavedIntent())
+                .create();
+    }
+
     /**
      * Verifies {@link Notification#contentIntent} can be intercepted by {@link
      * NotificationIntentInterceptor}.
@@ -145,8 +151,7 @@
         Notification notification = mShadowNotificationManager.getAllNotifications().get(0);
         Assert.assertEquals(TEST_NOTIFICATION_TITLE,
                 notification.extras.getCharSequence(Notification.EXTRA_TITLE).toString());
-        notification.contentIntent.send();
-        Robolectric.flushForegroundThreadScheduler();
+        sendPendingIntent(notification.contentIntent);
 
         // Verify the intent and histograms recorded.
         Intent receivedIntent = mReceiver.intentReceived();
@@ -172,8 +177,7 @@
         Notification notification = mShadowNotificationManager.getAllNotifications().get(0);
         Assert.assertEquals(TEST_NOTIFICATION_TITLE,
                 notification.extras.getCharSequence(Notification.EXTRA_TITLE).toString());
-        notification.deleteIntent.send();
-        Robolectric.flushForegroundThreadScheduler();
+        sendPendingIntent(notification.deleteIntent);
 
         // Verify the histogram.
         Assert.assertEquals(1,
@@ -200,9 +204,7 @@
         Assert.assertEquals(1, notification.actions.length);
         Notification.Action action = notification.actions[0];
         Assert.assertNotNull(action.actionIntent);
-        action.actionIntent.send();
-
-        Robolectric.flushForegroundThreadScheduler();
+        sendPendingIntent(action.actionIntent);
 
         // Verify the intent and histograms recorded.
         Intent receivedIntent = mReceiver.intentReceived();
diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn
index aa43fd6..33da361 100644
--- a/chrome/android/monochrome/BUILD.gn
+++ b/chrome/android/monochrome/BUILD.gn
@@ -11,6 +11,11 @@
   data_deps = [
     "//android_webview:system_webview_apk",
     "//build/android:devil_chromium_py",
+
+    # Depending on logdog_wrapper here is a hack to allow all isolated scripts
+    # to be wrapped, otherwise some python dependencies are missing, since
+    # this target doesn't depend on test_runner_py like others
+    "//build/android:logdog_wrapper_py",
     "//chrome/android:chrome_public_apk",
     "//chrome/android:monochrome_public_apk",
     "//testing:run_isolated_script_test",
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index dfce54a..085d480 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -443,6 +443,7 @@
 // Views debug commands.
 #define IDC_DEBUG_TOGGLE_TABLET_MODE 52510
 #define IDC_DEBUG_PRINT_VIEW_TREE 52511
+#define IDC_DEBUG_PRINT_VIEW_TREE_DETAILS 52512
 // Please leave a gap here for new debug commands.
 
 // NOTE: The last valid command value is 57343 (0xDFFF)
diff --git a/chrome/app/os_settings_search_tag_strings.grdp b/chrome/app/os_settings_search_tag_strings.grdp
index 1c28610..f0fc212 100644
--- a/chrome/app/os_settings_search_tag_strings.grdp
+++ b/chrome/app/os_settings_search_tag_strings.grdp
@@ -1174,12 +1174,21 @@
   <message name="IDS_OS_SETTINGS_TAG_ABOUT_TERMS_OF_SERVICE" desc="Text for search result item which, when clicked, navigates the user to 'About Chrome OS' settings, with a link to the Terms of Service.">
     Terms of Service
   </message>
-  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Troubleshooting'">
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Troubleshooting', 'Battery Health', 'CPU Usage', 'Memory Usage'">
     Diagnostics
   </message>
-  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics'">
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics', 'Battery Health', 'CPU Usage', 'Memory Usage'">
     Troubleshooting
   </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT2" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics', 'Troubleshooting', 'CPU Usage', 'Memory Usage'">
+    Battery Health
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT3" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics', 'Troubleshooting', 'Battery Health', 'Memory Usage'">
+    CPU Usage
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics', 'Troubleshooting', 'Battery Health', 'CPU Usage'">
+    Memory Usage
+  </message>
 
   <!-- On Startup section. -->
   <message name="IDS_OS_SETTINGS_TAG_ON_STARTUP" desc="Text for search result item which, when clicked, navigates the user to On Startup settings, with a radio group to configure the restore apps and pages options">
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT2.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT2.png.sha1
new file mode 100644
index 0000000..701f4039
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT2.png.sha1
@@ -0,0 +1 @@
+c1d89b2f357083a5cb2e791e65847d7ed89d7bfc
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT3.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT3.png.sha1
new file mode 100644
index 0000000..fda7d552
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT3.png.sha1
@@ -0,0 +1 @@
+3e5f92a6e48386c10f5b48f1b424be9a00e2bfcb
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4.png.sha1
new file mode 100644
index 0000000..e1b2edaa
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4.png.sha1
@@ -0,0 +1 @@
+f477a69ea1b255572da09472a0e470d16dc748d8
\ No newline at end of file
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 739cba6..cf37bc6 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -97,7 +97,7 @@
   </if>
 
   <!-- Main Page -->
-  <if expr="chromeos">
+  <if expr="chromeos or lacros">
     <!-- No target="_blank" because OS settings opens its own window. -->
     <message name="IDS_SETTINGS_OS_SETTINGS_BANNER" desc="Banner displayed in browser settings page that links to OS settings.">
       If a setting doesn't show on this page, look in your <ph name="LINK_BEGIN">&lt;a href="$1<ex>https://google.com/</ex>"&gt;</ph>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2fbbc05..0666cf8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -802,8 +802,6 @@
     "metrics/authenticator_utility.h",
     "metrics/bluetooth_available_utility.cc",
     "metrics/bluetooth_available_utility.h",
-    "metrics/browser_window_histogram_helper.cc",
-    "metrics/browser_window_histogram_helper.h",
     "metrics/chrome_browser_main_extra_parts_metrics.cc",
     "metrics/chrome_browser_main_extra_parts_metrics.h",
     "metrics/chrome_feature_list_creator.cc",
@@ -1067,6 +1065,8 @@
     "page_load_metrics/observers/translate_page_load_metrics_observer.h",
     "page_load_metrics/page_load_metrics_initialize.cc",
     "page_load_metrics/page_load_metrics_initialize.h",
+    "page_load_metrics/page_load_metrics_memory_tracker_factory.cc",
+    "page_load_metrics/page_load_metrics_memory_tracker_factory.h",
     "password_manager/account_password_store_factory.cc",
     "password_manager/account_password_store_factory.h",
     "password_manager/affiliation_service_factory.cc",
@@ -4003,6 +4003,8 @@
       "signin/signin_promo.h",
       "signin/signin_ui_util.cc",
       "signin/signin_ui_util.h",
+      "speech/chrome_speech_recognition_service.cc",
+      "speech/chrome_speech_recognition_service.h",
       "speech/extension_api/tts_extension_api_constants.cc",  # Should be moved
                                                               # to extensions
                                                               # section ?
@@ -4011,7 +4013,6 @@
       "speech/speech_recognition_client_browser_interface.h",
       "speech/speech_recognition_client_browser_interface_factory.cc",
       "speech/speech_recognition_client_browser_interface_factory.h",
-      "speech/speech_recognition_service.cc",
       "speech/speech_recognition_service.h",
       "speech/speech_recognition_service_factory.cc",
       "speech/speech_recognition_service_factory.h",
@@ -4208,6 +4209,16 @@
     ]
     if (is_chromeos_ash) {
       sources += [
+        "speech/cros_speech_recognition_service.cc",
+        "speech/cros_speech_recognition_service.h",
+        "speech/cros_speech_recognition_service_factory.cc",
+        "speech/cros_speech_recognition_service_factory.h",
+      ]
+
+      deps += [ "//chrome/services/speech:lib" ]
+    }
+    if (is_chromeos_ash) {
+      sources += [
         "sharing/webrtc/ice_config_fetcher.cc",
         "sharing/webrtc/ice_config_fetcher.h",
         "sharing/webrtc/sharing_mojo_service.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index e4a9555b..a9d34ba3 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -404,7 +404,7 @@
   "+third_party/widevine/cdm/buildflags.h",
   "+third_party/widevine/cdm/widevine_cdm_common.h",
   "+chrome/services/machine_learning",
-
+  "+chrome/services/speech",
   # Code under //ash should be accessed via its public API. See //ash/README.md.
   "-ash",
   "+ash/components",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f82532f..32f588f5 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1459,6 +1459,25 @@
     {"B", kTabHoverCardsSettingB, base::size(kTabHoverCardsSettingB), nullptr},
     {"C", kTabHoverCardsSettingC, base::size(kTabHoverCardsSettingC), nullptr}};
 
+const FeatureEntry::FeatureParam kMinimumTabWidthSettingPinned[] = {
+    {features::kMinimumTabWidthFeatureParameterName, "54"}};
+const FeatureEntry::FeatureParam kMinimumTabWidthSettingMedium[] = {
+    {features::kMinimumTabWidthFeatureParameterName, "72"}};
+const FeatureEntry::FeatureParam kMinimumTabWidthSettingLarge[] = {
+    {features::kMinimumTabWidthFeatureParameterName, "140"}};
+const FeatureEntry::FeatureParam kMinimumTabWidthSettingFull[] = {
+    {features::kMinimumTabWidthFeatureParameterName, "256"}};
+
+const FeatureEntry::FeatureVariation kTabScrollingVariations[] = {
+    {" - tabs shrink to pinned tab width", kMinimumTabWidthSettingPinned,
+     base::size(kMinimumTabWidthSettingPinned), nullptr},
+    {" - tabs shrink to a medium width", kMinimumTabWidthSettingMedium,
+     base::size(kMinimumTabWidthSettingMedium), nullptr},
+    {" - tabs shrink to a large width", kMinimumTabWidthSettingLarge,
+     base::size(kMinimumTabWidthSettingLarge), nullptr},
+    {" - tabs do not shrink", kMinimumTabWidthSettingFull,
+     base::size(kMinimumTabWidthSettingFull), nullptr}};
+
 const FeatureEntry::FeatureParam kPromoBrowserCommandUnknownCommandParam[] = {
     {features::kPromoBrowserCommandIdParam, "0"}};
 const FeatureEntry::FeatureParam
@@ -2941,9 +2960,6 @@
     {"stylus-battery-status", flag_descriptions::kStylusBatteryStatusName,
      flag_descriptions::kStylusBatteryStatusDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kStylusBatteryStatus)},
-    {"system-tray-mic-gain", flag_descriptions::kSystemTrayMicGainName,
-     flag_descriptions::kSystemTrayMicGainDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kSystemTrayMicGainSetting)},
     {"deprecate-low-usage-codecs",
      flag_descriptions::kDeprecateLowUsageCodecsName,
      flag_descriptions::kDeprecateLowUsageCodecsDescription, kOsCrOS,
@@ -3214,6 +3230,10 @@
      flag_descriptions::kExtensionContentVerificationName,
      flag_descriptions::kExtensionContentVerificationDescription, kOsDesktop,
      MULTI_VALUE_TYPE(kExtensionContentVerificationChoices)},
+    {"preemtive-link-to-text-generation",
+     flag_descriptions::kPreemtiveLinkToTextGenerationName,
+     flag_descriptions::kPreemtiveLinkToTextGenerationDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kPreemtiveLinkToTextGeneration)},
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"keyboard-based-display-arrangement-in-settings",
      flag_descriptions::kKeyboardBasedDisplayArrangementInSettingsName,
@@ -3909,6 +3929,9 @@
     {"arc-documents-provider", flag_descriptions::kArcDocumentsProviderName,
      flag_descriptions::kArcDocumentsProviderDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kEnableDocumentsProviderInFilesAppFeature)},
+    {"arc-enable-usap", flag_descriptions::kArcEnableUsapName,
+     flag_descriptions::kArcEnableUsapDesc, kOsCrOS,
+     FEATURE_VALUE_TYPE(arc::kEnableUsap)},
     {"arc-file-picker-experiment",
      flag_descriptions::kArcFilePickerExperimentName,
      flag_descriptions::kArcFilePickerExperimentDescription, kOsCrOS,
@@ -4520,7 +4543,9 @@
     {flag_descriptions::kScrollableTabStripFlagId,
      flag_descriptions::kScrollableTabStripName,
      flag_descriptions::kScrollableTabStripDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kScrollableTabStrip)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(features::kScrollableTabStrip,
+                                    kTabScrollingVariations,
+                                    "TabScrolling")},
 
     {"scrollable-tabstrip-buttons",
      flag_descriptions::kScrollableTabStripButtonsName,
@@ -5495,6 +5520,10 @@
      flag_descriptions::kEnablePalmOnToolTypePalmName, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnablePalmOnToolTypePalm)},
 
+    {"enable-pci-guard-ui", flag_descriptions::kEnablePciguardUiName,
+     flag_descriptions::kEnablePciguardUiDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kEnablePciguardUi)},
+
     {"enable-heuristic-stylus-palm-rejection",
      flag_descriptions::kEnableHeuristicStylusPalmRejectionName,
      flag_descriptions::kEnableHeuristicStylusPalmRejectionDescription, kOsCrOS,
@@ -5675,13 +5704,6 @@
      flag_descriptions::kAutofillPruneSuggestionsDescription, kOsAll,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillPruneSuggestions)},
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    {"enable-advanced-ppd-attributes",
-     flag_descriptions::kEnableAdvancedPpdAttributesName,
-     flag_descriptions::kEnableAdvancedPpdAttributesDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(printing::features::kAdvancedPpdAttributes)},
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
     {"allow-sync-xhr-in-page-dismissal",
      flag_descriptions::kAllowSyncXHRInPageDismissalName,
      flag_descriptions::kAllowSyncXHRInPageDismissalDescription,
diff --git a/chrome/browser/android/compositor/tab_content_manager.cc b/chrome/browser/android/compositor/tab_content_manager.cc
index de7b2c2..89eedee0 100644
--- a/chrome/browser/android/compositor/tab_content_manager.cc
+++ b/chrome/browser/android/compositor/tab_content_manager.cc
@@ -43,6 +43,8 @@
 
 namespace {
 
+const double kDefaultThumbnailAspectRatio = 0.85;
+
 using TabReadbackCallback = base::OnceCallback<void(float, const SkBitmap&)>;
 
 }  // namespace
@@ -72,7 +74,7 @@
     if (crop_to_match_aspect_ratio) {
       double aspect_ratio = base::GetFieldTrialParamByFeatureAsDouble(
           chrome::android::kTabGridLayoutAndroid, "thumbnail_aspect_ratio",
-          1.0);
+          kDefaultThumbnailAspectRatio);
       aspect_ratio = ThumbnailCache::clampAspectRatio(aspect_ratio, 0.5, 2.0);
       int height = std::min(view_size_in_pixels.height(),
                             (int)(view_size_in_pixels.width() / aspect_ratio));
@@ -130,7 +132,8 @@
                                      jboolean save_jpeg_thumbnails)
     : weak_java_tab_content_manager_(env, obj) {
   double jpeg_aspect_ratio = base::GetFieldTrialParamByFeatureAsDouble(
-      chrome::android::kTabGridLayoutAndroid, "thumbnail_aspect_ratio", 1.0);
+      chrome::android::kTabGridLayoutAndroid, "thumbnail_aspect_ratio",
+      kDefaultThumbnailAspectRatio);
   thumbnail_cache_ = std::make_unique<ThumbnailCache>(
       static_cast<size_t>(default_cache_size),
       static_cast<size_t>(approximation_cache_size),
@@ -393,7 +396,8 @@
     // landscape mode.
     int scale = need_downsampling ? 2 : 1;
     double aspect_ratio = base::GetFieldTrialParamByFeatureAsDouble(
-        chrome::android::kTabGridLayoutAndroid, "thumbnail_aspect_ratio", 1.0);
+        chrome::android::kTabGridLayoutAndroid, "thumbnail_aspect_ratio",
+        kDefaultThumbnailAspectRatio);
     aspect_ratio = ThumbnailCache::clampAspectRatio(aspect_ratio, 0.5, 2.0);
 
     int width = std::min(bitmap.width() / scale,
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index a18cad2..6f6435c1 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -255,12 +255,14 @@
   std::string search_url_full;
   std::string search_url_preload;
   int coca_card_tag = 0;
+  std::vector<std::string> related_searches;
 
   DecodeSearchTermFromJsonResponse(
       json_string, &search_term, &display_text, &alternate_term, &mid,
       &prevent_preload, &mention_start, &mention_end, &context_language,
       &thumbnail_url, &caption, &quick_action_uri, &quick_action_category,
-      &logged_event_id, &search_url_full, &search_url_preload, &coca_card_tag);
+      &logged_event_id, &search_url_full, &search_url_preload, &coca_card_tag,
+      &related_searches);
   if (mention_start != 0 || mention_end != 0) {
     // Sanity check that our selection is non-zero and it is less than
     // 100 characters as that would make contextual search bar hide.
@@ -283,7 +285,7 @@
       prevent_preload == kDoPreventPreloadValue, start_adjust, end_adjust,
       context_language, thumbnail_url, caption, quick_action_uri,
       quick_action_category, logged_event_id, search_url_full,
-      search_url_preload, coca_card_tag));
+      search_url_preload, coca_card_tag, related_searches));
 }
 
 std::string ContextualSearchDelegate::BuildRequestUrl(
@@ -445,7 +447,8 @@
     int64_t* logged_event_id,
     std::string* search_url_full,
     std::string* search_url_preload,
-    int* coca_card_tag) {
+    int* coca_card_tag,
+    std::vector<std::string>* related_searches) {
   bool contains_xssi_escape =
       base::StartsWith(response, kXssiEscape, base::CompareCase::SENSITIVE);
   const std::string& proper_json =
@@ -563,6 +566,18 @@
     search_query_list->GetString(0, display_text);
     search_query_list->GetString(0, search_term);
   }
+
+  // For Related Searches extract the array.
+  base::ListValue* searches_list = nullptr;
+  dict->GetList(kRelatedSearchesQueryList, &searches_list);
+  if (base::FeatureList::IsEnabled(chrome::android::kRelatedSearches) &&
+      searches_list && searches_list->GetSize() >= 1) {
+    for (size_t i = 0; i < searches_list->GetSize(); ++i) {
+      std::string search;
+      searches_list->GetString(i, &search);
+      related_searches->push_back(search);
+    }
+  }
 }
 
 // Extract the Start/End of the mentions in the surrounding text
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.h b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
index 5b21dca4..23c6dae 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
@@ -154,7 +154,8 @@
       int64_t* logged_event_id,
       std::string* search_url_full,
       std::string* search_url_preload,
-      int* coca_card_tag);
+      int* coca_card_tag,
+      std::vector<std::string>* related_searches);
 
   // Extracts the start and end location from a mentions list, and sets the
   // integers referenced by |startResult| and |endResult|.
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
index 5e170367..b176e7d 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
@@ -233,6 +233,7 @@
   std::string search_url_full() { return search_url_full_; }
   std::string search_url_preload() { return search_url_preload_; }
   int coca_card_tag() { return coca_card_tag_; }
+  std::vector<std::string> related_searches() { return related_searches_; }
 
   // The delegate under test.
   std::unique_ptr<ContextualSearchDelegate> delegate_;
@@ -260,6 +261,7 @@
     search_url_full_ = resolved_search_term.search_url_full;
     search_url_preload_ = resolved_search_term.search_url_preload;
     coca_card_tag_ = resolved_search_term.coca_card_tag;
+    related_searches_ = resolved_search_term.related_searches;
   }
 
   void recordSampleSelectionAvailable(const std::string& encoding,
@@ -288,6 +290,7 @@
   std::string search_url_full_;
   std::string search_url_preload_;
   int coca_card_tag_;
+  std::vector<std::string> related_searches_;
 
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
@@ -574,12 +577,14 @@
   std::string search_url_full;
   std::string search_url_preload;
   int coca_card_tag;
+  std::vector<std::string> related_searches;
 
   delegate_->DecodeSearchTermFromJsonResponse(
       json_with_escape, &search_term, &display_text, &alternate_term, &mid,
       &prevent_preload, &mention_start, &mention_end, &context_language,
       &thumbnail_url, &caption, &quick_action_uri, &quick_action_category,
-      &logged_event_id, &search_url_full, &search_url_preload, &coca_card_tag);
+      &logged_event_id, &search_url_full, &search_url_preload, &coca_card_tag,
+      &related_searches);
 
   EXPECT_EQ("obama", search_term);
   EXPECT_EQ("Barack Obama", display_text);
@@ -597,6 +602,7 @@
   EXPECT_EQ("https://www.google.com/search?q=define+obscure&ctxs=2&pf=c&sns=1",
             search_url_preload);
   EXPECT_EQ(12, coca_card_tag);
+  EXPECT_EQ(0u, related_searches.size());
 }
 
 TEST_F(ContextualSearchDelegateTest, ResponseWithLanguage) {
diff --git a/chrome/browser/android/contextualsearch/contextual_search_manager.cc b/chrome/browser/android/contextualsearch/contextual_search_manager.cc
index 7d1f4429..345d3d5 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_manager.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_manager.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <set>
 
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -126,6 +127,9 @@
   base::android::ScopedJavaLocalRef<jstring> j_search_url_preload =
       base::android::ConvertUTF8ToJavaString(
           env, resolved_search_term.search_url_preload);
+  base::android::ScopedJavaLocalRef<jobjectArray> j_searches =
+      base::android::ToJavaArrayOfStrings(
+          env, resolved_search_term.related_searches);
   Java_ContextualSearchManager_onSearchTermResolutionResponse(
       env, java_manager_, resolved_search_term.is_invalid,
       resolved_search_term.response_code, j_search_term, j_display_text,
@@ -135,7 +139,7 @@
       j_thumbnail_url, j_caption, j_quick_action_uri,
       resolved_search_term.quick_action_category,
       resolved_search_term.logged_event_id, j_search_url_full,
-      j_search_url_preload, resolved_search_term.coca_card_tag);
+      j_search_url_preload, resolved_search_term.coca_card_tag, j_searches);
 }
 
 void ContextualSearchManager::OnTextSurroundingSelectionAvailable(
diff --git a/chrome/browser/android/contextualsearch/resolved_search_term.cc b/chrome/browser/android/contextualsearch/resolved_search_term.cc
index 1150c62c..0f392e5 100644
--- a/chrome/browser/android/contextualsearch/resolved_search_term.cc
+++ b/chrome/browser/android/contextualsearch/resolved_search_term.cc
@@ -24,7 +24,8 @@
       logged_event_id(0),
       search_url_full(""),
       search_url_preload(""),
-      coca_card_tag(0) {}
+      coca_card_tag(0),
+      related_searches({}) {}
 
 ResolvedSearchTerm::ResolvedSearchTerm(
     bool is_invalid,
@@ -44,7 +45,8 @@
     int64_t logged_event_id,
     const std::string& search_url_full,
     const std::string& search_url_preload,
-    int coca_card_tag)
+    int coca_card_tag,
+    const std::vector<std::string>& related_searches)
     : is_invalid(is_invalid),
       response_code(response_code),
       search_term(search_term),
@@ -62,6 +64,7 @@
       logged_event_id(logged_event_id),
       search_url_full(search_url_full),
       search_url_preload(search_url_preload),
-      coca_card_tag(coca_card_tag) {}
+      coca_card_tag(coca_card_tag),
+      related_searches(related_searches) {}
 
 ResolvedSearchTerm::~ResolvedSearchTerm() {}
diff --git a/chrome/browser/android/contextualsearch/resolved_search_term.h b/chrome/browser/android/contextualsearch/resolved_search_term.h
index 194f77b..6043ce6 100644
--- a/chrome/browser/android/contextualsearch/resolved_search_term.h
+++ b/chrome/browser/android/contextualsearch/resolved_search_term.h
@@ -5,7 +5,23 @@
 #ifndef CHROME_BROWSER_ANDROID_CONTEXTUALSEARCH_RESOLVED_SEARCH_TERM_H_
 #define CHROME_BROWSER_ANDROID_CONTEXTUALSEARCH_RESOLVED_SEARCH_TERM_H_
 
-#include <string>
+#include <string.h>
+
+#include <algorithm>
+#include <cmath>
+#include <new>
+#include <ostream>
+#include <utility>
+
+#include "base/bit_cast.h"
+#include "base/containers/checked_iterators.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/memory_usage_estimator.h"
 
 #include "base/macros.h"
 
@@ -47,9 +63,12 @@
                      int64_t logged_event_id,
                      const std::string& search_url_full,
                      const std::string& search_url_preload,
-                     int coca_card_tag);
+                     int coca_card_tag,
+                     const std::vector<std::string>& related_searches);
   ~ResolvedSearchTerm();
 
+  // TODO(donnd): switch to member-initialization style instead of initializers.
+  // TODO(donnd): change these members names to include an ending underscore.
   const bool is_invalid;
   const int response_code;
   // Use strings, rather than just references, to keep this complete.
@@ -69,6 +88,7 @@
   const std::string search_url_full;
   const std::string search_url_preload;
   const int coca_card_tag;
+  std::vector<std::string> related_searches;
 
   DISALLOW_COPY_AND_ASSIGN(ResolvedSearchTerm);
 };
diff --git a/chrome/browser/android/contextualsearch/simple_search_term_resolver.cc b/chrome/browser/android/contextualsearch/simple_search_term_resolver.cc
index 72cff73..b4f0114 100644
--- a/chrome/browser/android/contextualsearch/simple_search_term_resolver.cc
+++ b/chrome/browser/android/contextualsearch/simple_search_term_resolver.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <set>
 
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -105,6 +106,9 @@
   base::android::ScopedJavaLocalRef<jstring> j_search_url_preload =
       base::android::ConvertUTF8ToJavaString(
           env, resolved_search_term.search_url_preload);
+  base::android::ScopedJavaLocalRef<jobjectArray> j_searches =
+      base::android::ToJavaArrayOfByteArray(
+          env, resolved_search_term.related_searches);
   Java_SimpleSearchTermResolver_onSearchTermResolutionResponse(
       env, java_instance_, resolved_search_term.is_invalid,
       resolved_search_term.response_code, j_search_term, j_display_text,
@@ -114,7 +118,7 @@
       j_thumbnail_url, j_caption, j_quick_action_uri,
       resolved_search_term.quick_action_category,
       resolved_search_term.logged_event_id, j_search_url_full,
-      j_search_url_preload, resolved_search_term.coca_card_tag);
+      j_search_url_preload, resolved_search_term.coca_card_tag, j_searches);
 }
 
 void SimpleSearchTermResolver::OnTextSurroundingSelectionAvailable(
diff --git a/chrome/browser/android/feed/v2/feed_stream_surface.cc b/chrome/browser/android/feed/v2/feed_stream_surface.cc
index f18c0da9..88eda1f2 100644
--- a/chrome/browser/android/feed/v2/feed_stream_surface.cc
+++ b/chrome/browser/android/feed/v2/feed_stream_surface.cc
@@ -46,7 +46,8 @@
 }
 
 FeedStreamSurface::FeedStreamSurface(const JavaRef<jobject>& j_this)
-    : feed_stream_api_(nullptr) {
+    : FeedStreamApi::SurfaceInterface(kInterestStream),
+      feed_stream_api_(nullptr) {
   java_ref_.Reset(j_this);
 
   FeedService* service = FeedServiceFactory::GetForBrowserContext(
@@ -95,9 +96,8 @@
   if (!feed_stream_api_)
     return;
   feed_stream_api_->LoadMore(
-      GetSurfaceId(),
-      base::BindOnce(&base::android::RunBooleanCallbackAndroid,
-                     ScopedJavaGlobalRef<jobject>(callback_obj)));
+      *this, base::BindOnce(&base::android::RunBooleanCallbackAndroid,
+                            ScopedJavaGlobalRef<jobject>(callback_obj)));
 }
 
 void FeedStreamSurface::ProcessThereAndBackAgain(
@@ -130,7 +130,8 @@
     return 0;
   std::string data_string;
   base::android::JavaByteArrayToString(env, data, &data_string);
-  return feed_stream_api_->CreateEphemeralChangeFromPackedData(data_string)
+  return feed_stream_api_
+      ->CreateEphemeralChangeFromPackedData(GetStreamType(), data_string)
       .GetUnsafeValue();
 }
 
@@ -139,7 +140,8 @@
                                               int change_id) {
   if (!feed_stream_api_)
     return;
-  feed_stream_api_->CommitEphemeralChange(EphemeralChangeId(change_id));
+  feed_stream_api_->CommitEphemeralChange(GetStreamType(),
+                                          EphemeralChangeId(change_id));
 }
 
 void FeedStreamSurface::DiscardEphemeralChange(JNIEnv* env,
@@ -147,7 +149,8 @@
                                                int change_id) {
   if (!feed_stream_api_)
     return;
-  feed_stream_api_->RejectEphemeralChange(EphemeralChangeId(change_id));
+  feed_stream_api_->RejectEphemeralChange(GetStreamType(),
+                                          EphemeralChangeId(change_id));
 }
 
 void FeedStreamSurface::SurfaceOpened(JNIEnv* env,
@@ -186,7 +189,7 @@
   if (!feed_stream_api_)
     return;
   feed_stream_api_->ReportOpenAction(
-      base::android::ConvertJavaStringToUTF8(env, slice_id));
+      GetStreamType(), base::android::ConvertJavaStringToUTF8(env, slice_id));
 }
 
 void FeedStreamSurface::ReportOpenInNewTabAction(
@@ -196,7 +199,7 @@
   if (!feed_stream_api_)
     return;
   feed_stream_api_->ReportOpenInNewTabAction(
-      base::android::ConvertJavaStringToUTF8(env, slice_id));
+      GetStreamType(), base::android::ConvertJavaStringToUTF8(env, slice_id));
 }
 
 void FeedStreamSurface::ReportSliceViewed(
@@ -206,7 +209,8 @@
   if (!feed_stream_api_)
     return;
   feed_stream_api_->ReportSliceViewed(
-      GetSurfaceId(), base::android::ConvertJavaStringToUTF8(env, slice_id));
+      GetSurfaceId(), GetStreamType(),
+      base::android::ConvertJavaStringToUTF8(env, slice_id));
 }
 
 void FeedStreamSurface::ReportFeedViewed(
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 6f0c60b..12935a6 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -28,6 +28,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/branding_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
@@ -221,6 +222,31 @@
 
 }  // namespace
 
+// Returns the last profile. This is extracted as a standalone function in order
+// to be friend with base::ScopedAllowBlocking.
+Profile* GetLastProfileMac() {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  if (!profile_manager)
+    return nullptr;
+
+  base::FilePath profile_path =
+      GetStartupProfilePath(profile_manager->user_data_dir(),
+                            /*current_directory=*/base::FilePath(),
+                            *base::CommandLine::ForCurrentProcess(),
+                            /*ignore_profile_picker=*/true);
+
+  // ProfileManager::GetProfile() is blocking if the profile was not loaded yet.
+  // TODO(https://1176734): Change this code to return nullptr when the profile
+  // is not loaded, and update all callers to handle this case.
+  base::ScopedAllowBlocking allow_blocking;
+
+  // lastProfile is used to open URLs passed in application:openFiles: and
+  // should not default to Guest, even if the profile picker is shown.
+  // TODO(https://crbug.com/1155158): Remove the ignore_profile_picker parameter
+  // once the picker supports opening URLs.
+  return profile_manager->GetProfile(profile_path);
+}
+
 @interface AppController () <HandoffActiveURLObserverBridgeDelegate>
 - (void)initMenuState;
 - (void)initProfileMenu;
@@ -1414,16 +1440,7 @@
   if (![self isProfileReady])
     return nullptr;
 
-  // On first launch, use the logic that ChromeBrowserMain uses to determine
-  // the initial profile.
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  if (!profile_manager)
-    return nullptr;
-
-  return profile_manager->GetProfile(
-      GetStartupProfilePath(profile_manager->user_data_dir(),
-                            /*current_directory=*/base::FilePath(),
-                            *base::CommandLine::ForCurrentProcess()));
+  return GetLastProfileMac();
 }
 
 - (Profile*)safeLastProfileForNewWindows {
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index 36e7d75..192f84ab 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/apps/app_service/app_icon_source.h"
 #include "chrome/browser/apps/app_service/app_service_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
@@ -254,10 +255,10 @@
 void AppServiceProxy::Launch(const std::string& app_id,
                              int32_t event_flags,
                              apps::mojom::LaunchSource launch_source,
-                             int64_t display_id) {
+                             apps::mojom::WindowInfoPtr window_info) {
   if (app_service_.is_connected()) {
     app_registry_cache_.ForOneApp(app_id, [this, event_flags, launch_source,
-                                           display_id](
+                                           &window_info](
                                               const apps::AppUpdate& update) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       if (MaybeShowLaunchPreventionDialog(update)) {
@@ -266,8 +267,9 @@
 #endif
 
       RecordAppLaunch(update.AppId(), launch_source);
+
       app_service_->Launch(update.AppType(), update.AppId(), event_flags,
-                           launch_source, display_id);
+                           launch_source, std::move(window_info));
     });
   }
 }
@@ -308,7 +310,7 @@
   LaunchAppWithIntent(
       app_id, event_flags,
       apps_util::CreateShareIntentFromFiles(file_urls, mime_types),
-      launch_source, display::kDefaultDisplayId);
+      launch_source, MakeWindowInfo(display::kDefaultDisplayId));
 }
 
 void AppServiceProxy::LaunchAppWithIntent(
@@ -316,10 +318,10 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   if (app_service_.is_connected()) {
     app_registry_cache_.ForOneApp(app_id, [this, event_flags, &intent,
-                                           launch_source, display_id](
+                                           launch_source, &window_info](
                                               const apps::AppUpdate& update) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       if (MaybeShowLaunchPreventionDialog(update)) {
@@ -327,9 +329,10 @@
       }
 #endif
       RecordAppLaunch(update.AppId(), launch_source);
+
       app_service_->LaunchAppWithIntent(update.AppType(), update.AppId(),
                                         event_flags, std::move(intent),
-                                        launch_source, display_id);
+                                        launch_source, std::move(window_info));
     });
   }
 }
@@ -338,9 +341,9 @@
                                        int32_t event_flags,
                                        GURL url,
                                        apps::mojom::LaunchSource launch_source,
-                                       int64_t display_id) {
+                                       apps::mojom::WindowInfoPtr window_info) {
   LaunchAppWithIntent(app_id, event_flags, apps_util::CreateIntentFromUrl(url),
-                      launch_source, display_id);
+                      launch_source, std::move(window_info));
 }
 
 void AppServiceProxy::SetPermission(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index 77f41a8..3e3f6ece 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -116,14 +116,12 @@
   // Launches the app for the given |app_id|. |event_flags| provides additional
   // context about the action which launches the app (e.g. a middle click
   // indicating opening a background tab). |launch_source| is the possible app
-  // launch sources, e.g. from Shelf, from the search box, etc. |display_id| is
-  // the id of the display from which the app is launched.
-  // display::kInvalidDisplayId means that the display does not exist or is not
-  // set.
+  // launch sources, e.g. from Shelf, from the search box, etc. |window_info| is
+  // the window information to launch an app, e.g. display_id, window bounds.
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id);
+              apps::mojom::WindowInfoPtr window_info = nullptr);
 
   // Launches the app for the given |app_id| with files from |file_paths|.
   // |event_flags| provides additional context about the action which launches
@@ -151,24 +149,24 @@
   // Launches an app for the given |app_id|, passing |intent| to the app.
   // |event_flags| provides additional context about the action which launch the
   // app (e.g. a middle click indicating opening a background tab).
-  // |launch_source| is the possible app launch sources. |display_id| is the id
-  // of the display from which the app is launched.
+  // |launch_source| is the possible app launch sources. |window_info| is the
+  // window information to launch an app, e.g. display_id, window bounds.
   void LaunchAppWithIntent(const std::string& app_id,
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id);
+                           apps::mojom::WindowInfoPtr window_info = nullptr);
 
   // Launches an app for the given |app_id|, passing |url| to the app.
   // |event_flags| provides additional context about the action which launch the
   // app (e.g. a middle click indicating opening a background tab).
-  // |launch_source| is the possible app launch sources. |display_id| is the id
-  // of the display from which the app is launched.
+  // |launch_source| is the possible app launch sources. |window_info| is the
+  // window information to launch an app, e.g. display_id, window bounds.
   void LaunchAppWithUrl(const std::string& app_id,
                         int32_t event_flags,
                         GURL url,
                         apps::mojom::LaunchSource launch_source,
-                        int64_t display_id);
+                        apps::mojom::WindowInfoPtr window_info = nullptr);
 
   // Sets |permission| for the app identified by |app_id|.
   void SetPermission(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index aec0e3b..aba8ae9 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -558,12 +558,14 @@
 void ArcApps::Launch(const std::string& app_id,
                      int32_t event_flags,
                      apps::mojom::LaunchSource launch_source,
-                     int64_t display_id) {
+                     apps::mojom::WindowInfoPtr window_info) {
   auto user_interaction_type = GetUserInterationType(launch_source);
   if (!user_interaction_type.has_value()) {
     return;
   }
 
+  int64_t display_id =
+      window_info ? window_info->display_id : display::kInvalidDisplayId;
   arc::LaunchApp(profile_, app_id, event_flags, user_interaction_type.value(),
                  display_id);
 
@@ -576,7 +578,7 @@
                                   int32_t event_flags,
                                   apps::mojom::IntentPtr intent,
                                   apps::mojom::LaunchSource launch_source,
-                                  int64_t display_id) {
+                                  apps::mojom::WindowInfoPtr window_info) {
   auto user_interaction_type = GetUserInterationType(launch_source);
   if (!user_interaction_type.has_value()) {
     return;
@@ -604,6 +606,9 @@
       activity->activity_name = intent->activity_name.value();
     }
 
+    int64_t display_id =
+        window_info ? window_info->display_id : display::kInvalidDisplayId;
+
     if (intent->mime_type.has_value() && intent->file_urls.has_value()) {
       const auto file_urls = intent->file_urls.value();
       arc::ConvertToContentUrlsAndShare(
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index 6257d27..23545016 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -89,12 +89,12 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void LaunchAppWithIntent(const std::string& app_id,
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission) override;
   void Uninstall(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/borealis_apps.cc b/chrome/browser/apps/app_service/borealis_apps.cc
index 49a5cc0..c8abd1b 100644
--- a/chrome/browser/apps/app_service/borealis_apps.cc
+++ b/chrome/browser/apps/app_service/borealis_apps.cc
@@ -170,7 +170,7 @@
 void BorealisApps::Launch(const std::string& app_id,
                           int32_t event_flags,
                           apps::mojom::LaunchSource launch_source,
-                          int64_t display_id) {
+                          apps::mojom::WindowInfoPtr window_info) {
   borealis::BorealisService::GetForProfile(profile_)->AppLauncher().Launch(
       app_id, base::DoNothing());
 }
diff --git a/chrome/browser/apps/app_service/borealis_apps.h b/chrome/browser/apps/app_service/borealis_apps.h
index 788a4e9cb..8e401b2 100644
--- a/chrome/browser/apps/app_service/borealis_apps.h
+++ b/chrome/browser/apps/app_service/borealis_apps.h
@@ -60,7 +60,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  bool clear_site_data,
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
index c6dd79d6..2b6f4fa 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -122,7 +122,7 @@
 void BuiltInChromeOsApps::Launch(const std::string& app_id,
                                  int32_t event_flags,
                                  apps::mojom::LaunchSource launch_source,
-                                 int64_t display_id) {
+                                 apps::mojom::WindowInfoPtr window_info) {
   if (app_id == ash::kInternalAppIdKeyboardShortcutViewer) {
     ash::ToggleKeyboardShortcutViewer();
   }
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
index 4b04f021..6cee114 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.h
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
@@ -41,7 +41,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void GetMenuModel(const std::string& app_id,
                     apps::mojom::MenuType menu_type,
                     int64_t display_id,
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index 661e353c..be16ba5 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -141,8 +141,10 @@
 void CrostiniApps::Launch(const std::string& app_id,
                           int32_t event_flags,
                           apps::mojom::LaunchSource launch_source,
-                          int64_t display_id) {
-  crostini::LaunchCrostiniApp(profile_, app_id, display_id);
+                          apps::mojom::WindowInfoPtr window_info) {
+  crostini::LaunchCrostiniApp(
+      profile_, app_id,
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
 }
 
 void CrostiniApps::Uninstall(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/crostini_apps.h b/chrome/browser/apps/app_service/crostini_apps.h
index 0b00d0e..935b593 100644
--- a/chrome/browser/apps/app_service/crostini_apps.h
+++ b/chrome/browser/apps/app_service/crostini_apps.h
@@ -58,7 +58,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  bool clear_site_data,
diff --git a/chrome/browser/apps/app_service/extension_apps_base.cc b/chrome/browser/apps/app_service/extension_apps_base.cc
index 244afd23..e8a74d1 100644
--- a/chrome/browser/apps/app_service/extension_apps_base.cc
+++ b/chrome/browser/apps/app_service/extension_apps_base.cc
@@ -295,7 +295,7 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   const auto* extension = MaybeGetExtension(app_id);
   if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_)) {
     return nullptr;
@@ -305,12 +305,14 @@
     RunExtensionEnableFlow(
         app_id, base::BindOnce(&ExtensionAppsBase::LaunchAppWithIntent,
                                weak_factory_.GetWeakPtr(), app_id, event_flags,
-                               std::move(intent), launch_source, display_id));
+                               std::move(intent), launch_source,
+                               std::move(window_info)));
     return nullptr;
   }
 
   auto params = apps::CreateAppLaunchParamsForIntent(
-      app_id, event_flags, GetAppLaunchSource(launch_source), display_id,
+      app_id, event_flags, GetAppLaunchSource(launch_source),
+      window_info ? window_info->display_id : display::kInvalidDisplayId,
       extensions::GetLaunchContainer(extensions::ExtensionPrefs::Get(profile_),
                                      extension),
       std::move(intent));
@@ -393,13 +395,17 @@
 void ExtensionAppsBase::Launch(const std::string& app_id,
                                int32_t event_flags,
                                apps::mojom::LaunchSource launch_source,
-                               int64_t display_id) {
+                               apps::mojom::WindowInfoPtr window_info) {
   const auto* extension = MaybeGetExtension(app_id);
-  if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_) ||
-      RunExtensionEnableFlow(
-          app_id,
-          base::BindOnce(&ExtensionAppsBase::Launch, weak_factory_.GetWeakPtr(),
-                         app_id, event_flags, launch_source, display_id))) {
+  if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_)) {
+    return;
+  }
+
+  if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
+    RunExtensionEnableFlow(
+        app_id, base::BindOnce(&ExtensionAppsBase::Launch,
+                               weak_factory_.GetWeakPtr(), app_id, event_flags,
+                               launch_source, std::move(window_info)));
     return;
   }
 
@@ -435,7 +441,7 @@
   // The app will be created for the currently active profile.
   AppLaunchParams params = CreateAppLaunchParamsWithEventFlags(
       profile_, extension, event_flags, GetAppLaunchSource(launch_source),
-      display_id);
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
   ash::ShelfLaunchSource source = ConvertLaunchSource(launch_source);
   if ((source == ash::LAUNCH_FROM_APP_LIST ||
        source == ash::LAUNCH_FROM_APP_LIST_SEARCH) &&
@@ -472,9 +478,9 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   LaunchAppWithIntentImpl(app_id, event_flags, std::move(intent), launch_source,
-                          display_id);
+                          std::move(window_info));
 }
 
 void ExtensionAppsBase::Uninstall(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/extension_apps_base.h b/chrome/browser/apps/app_service/extension_apps_base.h
index 5636c82..1d686e0e 100644
--- a/chrome/browser/apps/app_service/extension_apps_base.h
+++ b/chrome/browser/apps/app_service/extension_apps_base.h
@@ -82,7 +82,7 @@
       int32_t event_flags,
       apps::mojom::IntentPtr intent,
       apps::mojom::LaunchSource launch_source,
-      int64_t display_id);
+      apps::mojom::WindowInfoPtr window_info);
 
   virtual content::WebContents* LaunchImpl(AppLaunchParams&& params);
 
@@ -123,7 +123,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void LaunchAppWithFiles(const std::string& app_id,
                           apps::mojom::LaunchContainer container,
                           int32_t event_flags,
@@ -133,7 +133,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  bool clear_site_data,
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
index 93772dc..786f139 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
@@ -190,9 +190,9 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   auto* tab = LaunchAppWithIntentImpl(app_id, event_flags, std::move(intent),
-                                      launch_source, display_id);
+                                      launch_source, std::move(window_info));
 
   if (launch_source == apps::mojom::LaunchSource::kFromArc && tab) {
     // Add a flag to remember this tab originated in the ARC context.
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.h b/chrome/browser/apps/app_service/extension_apps_chromeos.h
index ad315bd..c8e63e9 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.h
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.h
@@ -80,7 +80,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void PauseApp(const std::string& app_id) override;
   void UnpauseApps(const std::string& app_id) override;
   void GetMenuModel(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/lacros_apps.cc b/chrome/browser/apps/app_service/lacros_apps.cc
index 5105559c..7769cc8 100644
--- a/chrome/browser/apps/app_service/lacros_apps.cc
+++ b/chrome/browser/apps/app_service/lacros_apps.cc
@@ -110,7 +110,7 @@
 void LacrosApps::Launch(const std::string& app_id,
                         int32_t event_flags,
                         apps::mojom::LaunchSource launch_source,
-                        int64_t display_id) {
+                        apps::mojom::WindowInfoPtr window_info) {
   DCHECK_EQ(extension_misc::kLacrosAppId, app_id);
   crosapi::BrowserManager::Get()->NewWindow();
 }
diff --git a/chrome/browser/apps/app_service/lacros_apps.h b/chrome/browser/apps/app_service/lacros_apps.h
index 7cd4e96d..65f7a56 100644
--- a/chrome/browser/apps/app_service/lacros_apps.h
+++ b/chrome/browser/apps/app_service/lacros_apps.h
@@ -50,7 +50,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void GetMenuModel(const std::string& app_id,
                     apps::mojom::MenuType menu_type,
                     int64_t display_id,
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index c9209a2a..67fd88d 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -240,4 +240,10 @@
   return browser->session_id().id();
 }
 
+apps::mojom::WindowInfoPtr MakeWindowInfo(int64_t display_id) {
+  apps::mojom::WindowInfoPtr window_info = apps::mojom::WindowInfo::New();
+  window_info->display_id = display_id;
+  return window_info;
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/launch_utils.h b/chrome/browser/apps/app_service/launch_utils.h
index 9aaa0c7..ecbd03f 100644
--- a/chrome/browser/apps/app_service/launch_utils.h
+++ b/chrome/browser/apps/app_service/launch_utils.h
@@ -76,6 +76,10 @@
 int GetSessionIdForRestoreFromWebContents(
     const content::WebContents* web_contents);
 
+// Helper to create apps::mojom::WindowInfoPtr using |display_id|, which is the
+// id of the display from which the app is launched.
+apps::mojom::WindowInfoPtr MakeWindowInfo(int64_t display_id);
+
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_UTILS_H_
diff --git a/chrome/browser/apps/app_service/notifications_browsertest.cc b/chrome/browser/apps/app_service/notifications_browsertest.cc
index df64d8b..7983c86 100644
--- a/chrome/browser/apps/app_service/notifications_browsertest.cc
+++ b/chrome/browser/apps/app_service/notifications_browsertest.cc
@@ -46,7 +46,6 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "ui/display/display.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
@@ -143,7 +142,7 @@
     ExtensionTestMessageListener launched_listener("launched", true);
     apps::AppServiceProxyFactory::GetForProfile(profile())->Launch(
         extension->id(), ui::EF_SHIFT_DOWN,
-        apps::mojom::LaunchSource::kFromTest, display::kInvalidDisplayId);
+        apps::mojom::LaunchSource::kFromTest);
     EXPECT_TRUE(launched_listener.WaitUntilSatisfied());
     launched_listener.Reply(create_window_options);
 
diff --git a/chrome/browser/apps/app_service/plugin_vm_apps.cc b/chrome/browser/apps/app_service/plugin_vm_apps.cc
index 55d4356..f88eb34 100644
--- a/chrome/browser/apps/app_service/plugin_vm_apps.cc
+++ b/chrome/browser/apps/app_service/plugin_vm_apps.cc
@@ -190,7 +190,7 @@
 void PluginVmApps::Launch(const std::string& app_id,
                           int32_t event_flags,
                           apps::mojom::LaunchSource launch_source,
-                          int64_t display_id) {
+                          apps::mojom::WindowInfoPtr window_info) {
   DCHECK_EQ(plugin_vm::kPluginVmShelfAppId, app_id);
   if (plugin_vm::PluginVmFeatures::Get()->IsEnabled(profile_)) {
     plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->LaunchPluginVm(
diff --git a/chrome/browser/apps/app_service/plugin_vm_apps.h b/chrome/browser/apps/app_service/plugin_vm_apps.h
index 87008695..0423614d 100644
--- a/chrome/browser/apps/app_service/plugin_vm_apps.h
+++ b/chrome/browser/apps/app_service/plugin_vm_apps.h
@@ -51,7 +51,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission) override;
   void Uninstall(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/remote_apps.cc b/chrome/browser/apps/app_service/remote_apps.cc
index e85a1b7..da96e46 100644
--- a/chrome/browser/apps/app_service/remote_apps.cc
+++ b/chrome/browser/apps/app_service/remote_apps.cc
@@ -111,7 +111,7 @@
 void RemoteApps::Launch(const std::string& app_id,
                         int32_t event_flags,
                         mojom::LaunchSource launch_source,
-                        int64_t display_id) {
+                        apps::mojom::WindowInfoPtr window_info) {
   delegate_->LaunchApp(app_id);
 }
 
diff --git a/chrome/browser/apps/app_service/remote_apps.h b/chrome/browser/apps/app_service/remote_apps.h
index 4e9af10d..32f40dc4 100644
--- a/chrome/browser/apps/app_service/remote_apps.h
+++ b/chrome/browser/apps/app_service/remote_apps.h
@@ -74,7 +74,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void GetMenuModel(const std::string& app_id,
                     apps::mojom::MenuType menu_type,
                     int64_t display_id,
diff --git a/chrome/browser/apps/app_service/web_apps_base.cc b/chrome/browser/apps/app_service/web_apps_base.cc
index 785bb11c..4ff1b001 100644
--- a/chrome/browser/apps/app_service/web_apps_base.cc
+++ b/chrome/browser/apps/app_service/web_apps_base.cc
@@ -261,7 +261,7 @@
 void WebAppsBase::Launch(const std::string& app_id,
                          int32_t event_flags,
                          apps::mojom::LaunchSource launch_source,
-                         int64_t display_id) {
+                         apps::mojom::WindowInfoPtr window_info) {
   if (!profile_) {
     return;
   }
@@ -310,7 +310,7 @@
 
   AppLaunchParams params = apps::CreateAppIdLaunchParamsWithEventFlags(
       web_app->app_id(), event_flags, GetAppLaunchSource(launch_source),
-      display_id,
+      window_info ? window_info->display_id : display::kInvalidDisplayId,
       /*fallback_container=*/
       web_app::ConvertDisplayModeToAppLaunchContainer(display_mode));
 
@@ -338,9 +338,10 @@
                                       int32_t event_flags,
                                       apps::mojom::IntentPtr intent,
                                       apps::mojom::LaunchSource launch_source,
-                                      int64_t display_id) {
-  LaunchAppWithIntentImpl(app_id, event_flags, std::move(intent), launch_source,
-                          display_id);
+                                      apps::mojom::WindowInfoPtr window_info) {
+  LaunchAppWithIntentImpl(
+      app_id, event_flags, std::move(intent), launch_source,
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
 }
 
 void WebAppsBase::SetPermission(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/web_apps_base.h b/chrome/browser/apps/app_service/web_apps_base.h
index 2e9325bb..f1437c9 100644
--- a/chrome/browser/apps/app_service/web_apps_base.h
+++ b/chrome/browser/apps/app_service/web_apps_base.h
@@ -108,7 +108,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void LaunchAppWithFiles(const std::string& app_id,
                           apps::mojom::LaunchContainer container,
                           int32_t event_flags,
@@ -118,7 +118,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission) override;
   void OpenNativeSettings(const std::string& app_id) override;
diff --git a/chrome/browser/apps/app_service/web_apps_base_browsertest.cc b/chrome/browser/apps/app_service/web_apps_base_browsertest.cc
index cc25c1f..b08b7ae 100644
--- a/chrome/browser/apps/app_service/web_apps_base_browsertest.cc
+++ b/chrome/browser/apps/app_service/web_apps_base_browsertest.cc
@@ -320,7 +320,8 @@
                           /*prefer_container=*/true);
   apps::AppServiceProxyFactory::GetForProfile(profile)->LaunchAppWithIntent(
       app_id, event_flags, std::move(intent),
-      apps::mojom::LaunchSource::kFromSharesheet, display::kDefaultDisplayId);
+      apps::mojom::LaunchSource::kFromSharesheet,
+      apps::MakeWindowInfo(display::kDefaultDisplayId));
   run_loop.Run();
 }
 
@@ -356,7 +357,8 @@
                           /*prefer_container=*/true);
   apps::AppServiceProxyFactory::GetForProfile(profile)->LaunchAppWithIntent(
       app_id, event_flags, std::move(intent),
-      apps::mojom::LaunchSource::kFromSharesheet, display::kDefaultDisplayId);
+      apps::mojom::LaunchSource::kFromSharesheet,
+      apps::MakeWindowInfo(display::kDefaultDisplayId));
   run_loop.Run();
 }
 
@@ -401,7 +403,7 @@
                           WindowOpenDisposition::NEW_WINDOW,
                           /*prefer_container=*/true);
   proxy->Launch(app_id, event_flags, apps::mojom::LaunchSource::kUnknown,
-                display::kDefaultDisplayId);
+                apps::MakeWindowInfo(display::kDefaultDisplayId));
   proxy->FlushMojoCallsForTesting();
 
   proxy->AppRegistryCache().ForOneApp(
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.cc b/chrome/browser/apps/app_service/web_apps_chromeos.cc
index a78c63c0..c37740a 100644
--- a/chrome/browser/apps/app_service/web_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/web_apps_chromeos.cc
@@ -152,9 +152,10 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   auto* tab = WebAppsChromeOs::LaunchAppWithIntentImpl(
-      app_id, event_flags, std::move(intent), launch_source, display_id);
+      app_id, event_flags, std::move(intent), launch_source,
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
 
   if (launch_source != apps::mojom::LaunchSource::kFromArc || !tab) {
     return;
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.h b/chrome/browser/apps/app_service/web_apps_chromeos.h
index 1ca66a9f..8644a9d 100644
--- a/chrome/browser/apps/app_service/web_apps_chromeos.h
+++ b/chrome/browser/apps/app_service/web_apps_chromeos.h
@@ -78,7 +78,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  bool clear_site_data,
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 8e66cf8..b8180b13 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -69,7 +69,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "printing/buildflags/buildflags.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/display/types/display_constants.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -525,7 +524,8 @@
 
 // Tests that extensions can't use platform-app-only APIs.
 IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, PlatformAppsOnly) {
-  ASSERT_TRUE(RunExtensionTestIgnoreManifestWarnings("platform_apps/apps_only"))
+  ASSERT_TRUE(RunExtensionTest({.name = "platform_apps/apps_only"},
+                               {.ignore_manifest_warnings = true}))
       << message_;
 }
 
@@ -1294,8 +1294,7 @@
                    apps::mojom::LaunchContainer::kLaunchContainerWindow,
                    WindowOpenDisposition::NEW_FOREGROUND_TAB,
                    true /* prefer_container */),
-               apps::mojom::LaunchSource::kFromTest,
-               display::kInvalidDisplayId);
+               apps::mojom::LaunchSource::kFromTest);
 
   while (!base::Contains(opener_app_ids_, file_manager->id())) {
     content::RunAllPendingInMessageLoop();
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 177ef05..6ed7c28b3 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -1170,6 +1170,41 @@
   sm_.Replay();
 }
 
+IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ClipboardCopySpeech) {
+  EnableChromeVox();
+  sm_.Call([this]() {
+    ui_test_utils::NavigateToURL(browser(),
+                                 GURL("data:text/html,<input autofocus "
+                                      "type='text' value='Foo'></input>"));
+  });
+
+  // The input is autofocused.
+  sm_.ExpectSpeech("Edit text");
+
+  // Select and copy the first character.
+  sm_.Call([this]() {
+    SendKeyPressWithShift(ui::VKEY_RIGHT);
+    SendKeyPressWithControl(ui::VKEY_C);
+  });
+  sm_.ExpectSpeech("copy F.");
+
+  // Select and copy the first two characters.
+  sm_.Call([this]() {
+    SendKeyPressWithShift(ui::VKEY_RIGHT);
+    SendKeyPressWithControl(ui::VKEY_C);
+  });
+  sm_.ExpectSpeech("copy Fo.");
+
+  // Select and copy all characters.
+  sm_.Call([this]() {
+    SendKeyPressWithShift(ui::VKEY_RIGHT);
+    SendKeyPressWithControl(ui::VKEY_C);
+  });
+  sm_.ExpectSpeech("copy Foo.");
+
+  sm_.Replay();
+}
+
 //
 // Spoken feedback tests of the out-of-box experience.
 //
diff --git a/chrome/browser/ash/app_mode/kiosk_app_update_service.cc b/chrome/browser/ash/app_mode/kiosk_app_update_service.cc
index 16f4b4c..c84f0d12 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_update_service.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_update_service.cc
@@ -7,9 +7,9 @@
 #include "base/logging.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/ash/app_mode/kiosk_app_update_service.h b/chrome/browser/ash/app_mode/kiosk_app_update_service.h
index 5c27470..44ba27c 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_update_service.h
+++ b/chrome/browser/ash/app_mode/kiosk_app_update_service.h
@@ -14,8 +14,8 @@
 #include "chrome/browser/ash/app_mode/kiosk_app_manager_observer.h"
 // TODO(https://crbug.com/1164001): forward declare when moved to
 // chrome/browser/ash/.
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager_observer.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager_observer.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/update_observer.h"
diff --git a/chrome/browser/ash/app_mode/kiosk_app_update_service_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_app_update_service_browsertest.cc
index 13ae0f7..8668a21 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_update_service_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_update_service_browsertest.cc
@@ -27,10 +27,10 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager_observer.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager_observer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/system/OWNERS b/chrome/browser/ash/system/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/system/OWNERS
rename to chrome/browser/ash/system/OWNERS
diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager.cc b/chrome/browser/ash/system/automatic_reboot_manager.cc
similarity index 99%
rename from chrome/browser/chromeos/system/automatic_reboot_manager.cc
rename to chrome/browser/ash/system/automatic_reboot_manager.cc
index 4af3026..88d7fdd 100644
--- a/chrome/browser/chromeos/system/automatic_reboot_manager.cc
+++ b/chrome/browser/ash/system/automatic_reboot_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
 
 #include <fcntl.h>
 #include <stddef.h>
diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager.h b/chrome/browser/ash/system/automatic_reboot_manager.h
similarity index 96%
rename from chrome/browser/chromeos/system/automatic_reboot_manager.h
rename to chrome/browser/ash/system/automatic_reboot_manager.h
index 34448e6..ec79068 100644
--- a/chrome/browser/chromeos/system/automatic_reboot_manager.h
+++ b/chrome/browser/ash/system/automatic_reboot_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
 
 #include <memory>
 
@@ -15,7 +15,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager_observer.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager_observer.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/update_engine_client.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -197,4 +197,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_H_
diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager_observer.h b/chrome/browser/ash/system/automatic_reboot_manager_observer.h
similarity index 79%
rename from chrome/browser/chromeos/system/automatic_reboot_manager_observer.h
rename to chrome/browser/ash/system/automatic_reboot_manager_observer.h
index 6ad9774a..7da17ba 100644
--- a/chrome/browser/chromeos/system/automatic_reboot_manager_observer.h
+++ b/chrome/browser/ash/system/automatic_reboot_manager_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
 
 namespace chromeos {
 namespace system {
@@ -36,4 +36,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_AUTOMATIC_REBOOT_MANAGER_OBSERVER_H_
diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc b/chrome/browser/ash/system/automatic_reboot_manager_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc
rename to chrome/browser/ash/system/automatic_reboot_manager_unittest.cc
index b6a03fc..3e8209f 100644
--- a/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc
+++ b/chrome/browser/ash/system/automatic_reboot_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
 
 #include <string>
 
@@ -23,9 +23,9 @@
 #include "base/time/tick_clock.h"
 #include "base/values.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager_observer.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager_observer.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/chromeos/system/breakpad_consent_watcher.cc b/chrome/browser/ash/system/breakpad_consent_watcher.cc
similarity index 96%
rename from chrome/browser/chromeos/system/breakpad_consent_watcher.cc
rename to chrome/browser/ash/system/breakpad_consent_watcher.cc
index bdf3338..e7eee765 100644
--- a/chrome/browser/chromeos/system/breakpad_consent_watcher.cc
+++ b/chrome/browser/ash/system/breakpad_consent_watcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/breakpad_consent_watcher.h"
+#include "chrome/browser/ash/system/breakpad_consent_watcher.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/system/breakpad_consent_watcher.h b/chrome/browser/ash/system/breakpad_consent_watcher.h
similarity index 90%
rename from chrome/browser/chromeos/system/breakpad_consent_watcher.h
rename to chrome/browser/ash/system/breakpad_consent_watcher.h
index 52dfa80..25d47824 100644
--- a/chrome/browser/chromeos/system/breakpad_consent_watcher.h
+++ b/chrome/browser/ash/system/breakpad_consent_watcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
 
 #include <memory>
 
@@ -52,4 +52,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_BREAKPAD_CONSENT_WATCHER_H_
diff --git a/chrome/browser/chromeos/system/device_disabling_browsertest.cc b/chrome/browser/ash/system/device_disabling_browsertest.cc
similarity index 99%
rename from chrome/browser/chromeos/system/device_disabling_browsertest.cc
rename to chrome/browser/ash/system/device_disabling_browsertest.cc
index a12ef8f..4d3e2e52 100644
--- a/chrome/browser/chromeos/system/device_disabling_browsertest.cc
+++ b/chrome/browser/ash/system/device_disabling_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/login_wizard.h"
@@ -25,7 +26,6 @@
 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
 #include "chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
diff --git a/chrome/browser/chromeos/system/device_disabling_manager.cc b/chrome/browser/ash/system/device_disabling_manager.cc
similarity index 98%
rename from chrome/browser/chromeos/system/device_disabling_manager.cc
rename to chrome/browser/ash/system/device_disabling_manager.cc
index 89c47e11..5ba4a96 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager.cc
+++ b/chrome/browser/ash/system/device_disabling_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
diff --git a/chrome/browser/chromeos/system/device_disabling_manager.h b/chrome/browser/ash/system/device_disabling_manager.h
similarity index 96%
rename from chrome/browser/chromeos/system/device_disabling_manager.h
rename to chrome/browser/ash/system/device_disabling_manager.h
index cc293495..35652c0 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager.h
+++ b/chrome/browser/ash/system/device_disabling_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_H_
 
 #include <memory>
 #include <string>
@@ -154,4 +154,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_H_
diff --git a/chrome/browser/chromeos/system/device_disabling_manager_default_delegate.cc b/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
similarity index 91%
rename from chrome/browser/chromeos/system/device_disabling_manager_default_delegate.cc
rename to chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
index fd2454d..98e5eb0 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager_default_delegate.cc
+++ b/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h"
+#include "chrome/browser/ash/system/device_disabling_manager_default_delegate.h"
 
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
diff --git a/chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h b/chrome/browser/ash/system/device_disabling_manager_default_delegate.h
similarity index 66%
rename from chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h
rename to chrome/browser/ash/system/device_disabling_manager_default_delegate.h
index 2316e70..2a782c8 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h
+++ b/chrome/browser/ash/system/device_disabling_manager_default_delegate.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
+#define CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 
 namespace chromeos {
 namespace system {
@@ -27,4 +27,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_DEVICE_DISABLING_MANAGER_DEFAULT_DELEGATE_H_
diff --git a/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/system/device_disabling_manager_unittest.cc
rename to chrome/browser/ash/system/device_disabling_manager_unittest.cc
index b01086b..428a446 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc
+++ b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
diff --git a/chrome/browser/chromeos/system/fake_input_device_settings.cc b/chrome/browser/ash/system/fake_input_device_settings.cc
similarity index 98%
rename from chrome/browser/chromeos/system/fake_input_device_settings.cc
rename to chrome/browser/ash/system/fake_input_device_settings.cc
index 7842d3e..95616ae4 100644
--- a/chrome/browser/chromeos/system/fake_input_device_settings.cc
+++ b/chrome/browser/ash/system/fake_input_device_settings.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/fake_input_device_settings.h"
+#include "chrome/browser/ash/system/fake_input_device_settings.h"
 
 #include <utility>
 
diff --git a/chrome/browser/chromeos/system/fake_input_device_settings.h b/chrome/browser/ash/system/fake_input_device_settings.h
similarity index 91%
rename from chrome/browser/chromeos/system/fake_input_device_settings.h
rename to chrome/browser/ash/system/fake_input_device_settings.h
index 56c9106a..f51fb36b 100644
--- a/chrome/browser/chromeos/system/fake_input_device_settings.h
+++ b/chrome/browser/ash/system/fake_input_device_settings.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
+#define CHROME_BROWSER_ASH_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 
 namespace chromeos {
 namespace system {
@@ -72,4 +72,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_FAKE_INPUT_DEVICE_SETTINGS_H_
diff --git a/chrome/browser/chromeos/system/input_device_settings.cc b/chrome/browser/ash/system/input_device_settings.cc
similarity index 99%
rename from chrome/browser/chromeos/system/input_device_settings.cc
rename to chrome/browser/ash/system/input_device_settings.cc
index b695ebff..4fb62c1b 100644
--- a/chrome/browser/chromeos/system/input_device_settings.cc
+++ b/chrome/browser/ash/system/input_device_settings.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 
 #include "chrome/browser/chromeos/policy/enrollment_requisition_manager.h"
 #include "chromeos/system/statistics_provider.h"
diff --git a/chrome/browser/chromeos/system/input_device_settings.h b/chrome/browser/ash/system/input_device_settings.h
similarity index 97%
rename from chrome/browser/chromeos/system/input_device_settings.h
rename to chrome/browser/ash/system/input_device_settings.h
index f222d4d..2d110b44 100644
--- a/chrome/browser/chromeos/system/input_device_settings.h
+++ b/chrome/browser/ash/system/input_device_settings.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_INPUT_DEVICE_SETTINGS_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_INPUT_DEVICE_SETTINGS_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_INPUT_DEVICE_SETTINGS_H_
+#define CHROME_BROWSER_ASH_SYSTEM_INPUT_DEVICE_SETTINGS_H_
 
 #include "base/callback_forward.h"
 #include "base/optional.h"
@@ -310,4 +310,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_INPUT_DEVICE_SETTINGS_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_INPUT_DEVICE_SETTINGS_H_
diff --git a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc b/chrome/browser/ash/system/input_device_settings_impl_ozone.cc
similarity index 98%
rename from chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
rename to chrome/browser/ash/system/input_device_settings_impl_ozone.cc
index dd79f35..3507ba54 100644
--- a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
+++ b/chrome/browser/ash/system/input_device_settings_impl_ozone.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "chrome/browser/ash/system/fake_input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chrome/browser/chromeos/system/fake_input_device_settings.h"
 #include "chromeos/system/devicemode.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/ozone/public/input_controller.h"
diff --git a/chrome/browser/chromeos/system/pointer_device_observer.cc b/chrome/browser/ash/system/pointer_device_observer.cc
similarity index 94%
rename from chrome/browser/chromeos/system/pointer_device_observer.cc
rename to chrome/browser/ash/system/pointer_device_observer.cc
index 709b4914..4bbcfc6 100644
--- a/chrome/browser/chromeos/system/pointer_device_observer.cc
+++ b/chrome/browser/ash/system/pointer_device_observer.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ash/system/pointer_device_observer.h"
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/events/devices/device_data_manager.h"
 
diff --git a/chrome/browser/chromeos/system/pointer_device_observer.h b/chrome/browser/ash/system/pointer_device_observer.h
similarity index 88%
rename from chrome/browser/chromeos/system/pointer_device_observer.h
rename to chrome/browser/ash/system/pointer_device_observer.h
index 58dca6bc..eb2d7410 100644
--- a/chrome/browser/chromeos/system/pointer_device_observer.h
+++ b/chrome/browser/ash/system/pointer_device_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_POINTER_DEVICE_OBSERVER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_POINTER_DEVICE_OBSERVER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_POINTER_DEVICE_OBSERVER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_POINTER_DEVICE_OBSERVER_H_
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -61,4 +61,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_POINTER_DEVICE_OBSERVER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_POINTER_DEVICE_OBSERVER_H_
diff --git a/chrome/browser/chromeos/system/procfs_util.cc b/chrome/browser/ash/system/procfs_util.cc
similarity index 98%
rename from chrome/browser/chromeos/system/procfs_util.cc
rename to chrome/browser/ash/system/procfs_util.cc
index a56ba423..b58514ae 100644
--- a/chrome/browser/chromeos/system/procfs_util.cc
+++ b/chrome/browser/ash/system/procfs_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/procfs_util.h"
+#include "chrome/browser/ash/system/procfs_util.h"
 
 #include "base/files/file_util.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chrome/browser/chromeos/system/procfs_util.h b/chrome/browser/ash/system/procfs_util.h
similarity index 91%
rename from chrome/browser/chromeos/system/procfs_util.h
rename to chrome/browser/ash/system/procfs_util.h
index b8b77d96..b73c1e6 100644
--- a/chrome/browser/chromeos/system/procfs_util.h
+++ b/chrome/browser/ash/system/procfs_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_PROCFS_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_PROCFS_UTIL_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_PROCFS_UTIL_H_
+#define CHROME_BROWSER_ASH_SYSTEM_PROCFS_UTIL_H_
 
 #include "base/files/file_path.h"
 #include "base/values.h"
@@ -61,4 +61,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_PROCFS_UTIL_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_PROCFS_UTIL_H_
diff --git a/chrome/browser/chromeos/system/procfs_util_unittest.cc b/chrome/browser/ash/system/procfs_util_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/system/procfs_util_unittest.cc
rename to chrome/browser/ash/system/procfs_util_unittest.cc
index 4a27a133..3cf41b1 100644
--- a/chrome/browser/chromeos/system/procfs_util_unittest.cc
+++ b/chrome/browser/ash/system/procfs_util_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#include "chrome/browser/chromeos/system/procfs_util.h"
+#include "chrome/browser/ash/system/procfs_util.h"
 
 namespace chromeos {
 namespace system {
diff --git a/chrome/browser/chromeos/system/system_clock.cc b/chrome/browser/ash/system/system_clock.cc
similarity index 98%
rename from chrome/browser/chromeos/system/system_clock.cc
rename to chrome/browser/ash/system/system_clock.cc
index 5913cb0..6586657b 100644
--- a/chrome/browser/chromeos/system/system_clock.cc
+++ b/chrome/browser/ash/system/system_clock.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/system_clock.h"
+#include "chrome/browser/ash/system/system_clock.h"
 
 #include <memory>
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/system_clock_observer.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/system_clock_observer.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/settings/cros_settings_names.h"
diff --git a/chrome/browser/chromeos/system/system_clock.h b/chrome/browser/ash/system/system_clock.h
similarity index 94%
rename from chrome/browser/chromeos/system/system_clock.h
rename to chrome/browser/ash/system/system_clock.h
index c1e54d0..e065eed 100644
--- a/chrome/browser/chromeos/system/system_clock.h
+++ b/chrome/browser/ash/system/system_clock.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_H_
+#define CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_H_
 
 #include <memory>
 
@@ -101,4 +101,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_H_
diff --git a/chrome/browser/chromeos/system/system_clock_observer.cc b/chrome/browser/ash/system/system_clock_observer.cc
similarity index 82%
rename from chrome/browser/chromeos/system/system_clock_observer.cc
rename to chrome/browser/ash/system/system_clock_observer.cc
index 52a8dc1..53eedf83 100644
--- a/chrome/browser/chromeos/system/system_clock_observer.cc
+++ b/chrome/browser/ash/system/system_clock_observer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/system_clock_observer.h"
+#include "chrome/browser/ash/system/system_clock_observer.h"
 
 namespace chromeos {
 namespace system {
diff --git a/chrome/browser/chromeos/system/system_clock_observer.h b/chrome/browser/ash/system/system_clock_observer.h
similarity index 71%
rename from chrome/browser/chromeos/system/system_clock_observer.h
rename to chrome/browser/ash/system/system_clock_observer.h
index 08f4494f..1d9b984 100644
--- a/chrome/browser/chromeos/system/system_clock_observer.h
+++ b/chrome/browser/ash/system/system_clock_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
 
 #include "base/macros.h"
 
@@ -22,4 +22,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_SYSTEM_CLOCK_OBSERVER_H_
diff --git a/chrome/browser/chromeos/system/timezone_resolver_manager.cc b/chrome/browser/ash/system/timezone_resolver_manager.cc
similarity index 98%
rename from chrome/browser/chromeos/system/timezone_resolver_manager.cc
rename to chrome/browser/ash/system/timezone_resolver_manager.cc
index 196b811..d568f89f 100644
--- a/chrome/browser/chromeos/system/timezone_resolver_manager.cc
+++ b/chrome/browser/ash/system/timezone_resolver_manager.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/command_line.h"
 #include "base/notreached.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/preferences.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/chromeos/system/timezone_resolver_manager.h b/chrome/browser/ash/system/timezone_resolver_manager.h
similarity index 94%
rename from chrome/browser/chromeos/system/timezone_resolver_manager.h
rename to chrome/browser/ash/system/timezone_resolver_manager.h
index 02be20d..193c8c5 100644
--- a/chrome/browser/chromeos/system/timezone_resolver_manager.h
+++ b/chrome/browser/ash/system/timezone_resolver_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -112,4 +112,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_RESOLVER_MANAGER_H_
diff --git a/chrome/browser/chromeos/system/timezone_util.cc b/chrome/browser/ash/system/timezone_util.cc
similarity index 98%
rename from chrome/browser/chromeos/system/timezone_util.cc
rename to chrome/browser/ash/system/timezone_util.cc
index 6c50bb4..3987ee3a 100644
--- a/chrome/browser/chromeos/system/timezone_util.cc
+++ b/chrome/browser/ash/system/timezone_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 
 #include <stddef.h>
 
@@ -21,11 +21,11 @@
 #include "base/synchronization/lock.h"
 #include "base/values.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/chromeos/system/timezone_util.h b/chrome/browser/ash/system/timezone_util.h
similarity index 93%
rename from chrome/browser/chromeos/system/timezone_util.h
rename to chrome/browser/ash/system/timezone_util.h
index 81a4ee4..98ec1580 100644
--- a/chrome/browser/chromeos/system/timezone_util.h
+++ b/chrome/browser/ash/system/timezone_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_UTIL_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_UTIL_H_
+#define CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_UTIL_H_
 
 #include <memory>
 
@@ -74,4 +74,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_TIMEZONE_UTIL_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_TIMEZONE_UTIL_H_
diff --git a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc b/chrome/browser/ash/system/tray_accessibility_browsertest.cc
similarity index 100%
rename from chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
rename to chrome/browser/ash/system/tray_accessibility_browsertest.cc
diff --git a/chrome/browser/chromeos/system/user_removal_manager.cc b/chrome/browser/ash/system/user_removal_manager.cc
similarity index 97%
rename from chrome/browser/chromeos/system/user_removal_manager.cc
rename to chrome/browser/ash/system/user_removal_manager.cc
index 47fdcb4..d67d02c 100644
--- a/chrome/browser/chromeos/system/user_removal_manager.cc
+++ b/chrome/browser/ash/system/user_removal_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/user_removal_manager.h"
+#include "chrome/browser/ash/system/user_removal_manager.h"
 
 #include <utility>
 
diff --git a/chrome/browser/chromeos/system/user_removal_manager.h b/chrome/browser/ash/system/user_removal_manager.h
similarity index 88%
rename from chrome/browser/chromeos/system/user_removal_manager.h
rename to chrome/browser/ash/system/user_removal_manager.h
index 5ea2a4dc..3ad83a7 100644
--- a/chrome/browser/chromeos/system/user_removal_manager.h
+++ b/chrome/browser/ash/system/user_removal_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_USER_REMOVAL_MANAGER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_USER_REMOVAL_MANAGER_H_
 
 #include "base/callback_forward.h"
 
@@ -40,4 +40,4 @@
 }  // namespace user_removal_manager
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_USER_REMOVAL_MANAGER_H_
diff --git a/chrome/browser/chromeos/system/user_removal_manager_unittest.cc b/chrome/browser/ash/system/user_removal_manager_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/system/user_removal_manager_unittest.cc
rename to chrome/browser/ash/system/user_removal_manager_unittest.cc
index 12ae8ea..4d923a4e 100644
--- a/chrome/browser/chromeos/system/user_removal_manager_unittest.cc
+++ b/chrome/browser/ash/system/user_removal_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system/user_removal_manager.h"
+#include "chrome/browser/ash/system/user_removal_manager.h"
 
 #include <memory>
 
diff --git a/chrome/browser/badging/OWNERS b/chrome/browser/badging/OWNERS
index 514fa2a..dfa01f6 100644
--- a/chrome/browser/badging/OWNERS
+++ b/chrome/browser/badging/OWNERS
@@ -1,2 +1,4 @@
-cmumford@google.com
+cmp@chromium.org
+dmurph@chromiumr.org
+msw@chromium.org
 mgiuca@chromium.org
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index f198517..39fa5ef0 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -13,6 +13,12 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager_default_delegate.h"
+#include "chrome/browser/ash/system/system_clock.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/chromeos/login/session/chrome_session_manager.h"
@@ -22,12 +28,6 @@
 #include "chrome/browser/chromeos/policy/system_proxy_manager.h"
 #include "chrome/browser/chromeos/scheduler_configuration_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/component_updater/metadata_table_chromeos.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1c1e25c..f8ea6dd8 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -531,6 +531,10 @@
         <include name="IDR_MACHINE_LEARNING_INTERNALS_TIME_MOJO_JS" file="${root_gen_dir}\mojo/public/mojom/base/time.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_MACHINE_LEARNING_INTERNALS_UTILS_JS" file="resources\chromeos\machine_learning\machine_learning_internals_utils.js" type="BINDATA" />
       </if>
+      <if expr="not is_android">
+        <!-- Chrome Cart -->
+        <part file="cart/resources/cart_resources.grdp" />
+      </if>
     </includes>
   </release>
 </grit>
diff --git a/chrome/browser/capability_delegation_browsertest.cc b/chrome/browser/capability_delegation_browsertest.cc
index c02dea0..7c584bf 100644
--- a/chrome/browser/capability_delegation_browsertest.cc
+++ b/chrome/browser/capability_delegation_browsertest.cc
@@ -60,19 +60,25 @@
   EXPECT_EQ(cross_site_url, frame_host->GetLastCommittedURL());
   EXPECT_TRUE(frame_host->IsCrossProcessSubframe());
 
-  // TODO(mustaq): We need to duplicate the following checks to include the
-  // cases without any user activation.  Calling |EvalJs| with the option
-  // EXECUTE_SCRIPT_NO_USER_GESTURE is not enough because the
-  // |NavigateIframeToURL| call above activates the top frame, perhaps to allow
-  // the navigation to complete.
+  // Without either user activation or payment request token, PaymentRequest
+  // dialog is not allowed.
+  EXPECT_EQ("NotAllowedError",
+            content::EvalJs(active_web_contents, "sendRequestToSubframe(false)",
+                            content::EXECUTE_SCRIPT_NO_USER_GESTURE));
 
-  // Without payment request token, PaymentRequest dialog is not allowed.
+  // Without user activation but with the delegation option, PaymentRequest
+  // dialog is not allowed.
+  EXPECT_EQ("NotAllowedError",
+            content::EvalJs(active_web_contents, "sendRequestToSubframe(true)",
+                            content::EXECUTE_SCRIPT_NO_USER_GESTURE));
+
+  // With user activation but without the delegation option, PaymentRequest
+  // dialog is not allowed.
   EXPECT_EQ("NotAllowedError", content::EvalJs(active_web_contents,
                                                "sendRequestToSubframe(false)"));
 
-  // With payment request token (plus user activation from |NavigateIframeToURL|
-  // above), PaymentRequest dialog is shown and then successfully aborted by the
-  // script.
+  // With both user activation and the delegation option, PaymentRequest dialog
+  // is shown and then successfully aborted by the script.
   EXPECT_EQ("AbortError", content::EvalJs(active_web_contents,
                                           "sendRequestToSubframe(true)"));
 }
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc
index c5e135d..06d4da27 100644
--- a/chrome/browser/cart/cart_service.cc
+++ b/chrome/browser/cart/cart_service.cc
@@ -3,12 +3,15 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/cart/cart_service.h"
+#include "base/json/json_reader.h"
 #include "chrome/browser/cart/cart_db_content.pb.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/browser_resources.h"
 #include "components/prefs/pref_service.h"
 #include "components/search/ntp_features.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "ui/base/resource/resource_bundle.h"
 
 namespace {
 constexpr char kFakeDataPrefix[] = "Fake:";
@@ -31,6 +34,14 @@
                                   CartDB::KeyAndValue pair2) {
   return pair1.second.timestamp() > pair2.second.timestamp();
 }
+
+base::Optional<base::Value> JSONToDictionary(int resource_id) {
+  base::StringPiece json_resource(
+      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id));
+  base::Optional<base::Value> value = base::JSONReader::Read(json_resource);
+  DCHECK(value && value.has_value() && value->is_dict());
+  return value;
+}
 }  // namespace
 
 CartService::CartService(Profile* profile)
@@ -38,7 +49,10 @@
       cart_db_(std::make_unique<CartDB>(profile_)),
       history_service_(HistoryServiceFactory::GetForProfile(
           profile_,
-          ServiceAccessType::EXPLICIT_ACCESS)) {
+          ServiceAccessType::EXPLICIT_ACCESS)),
+      domain_name_mapping_(JSONToDictionary(IDR_CART_DOMAIN_NAME_MAPPING_JSON)),
+      domain_cart_url_mapping_(
+          JSONToDictionary(IDR_CART_DOMAIN_CART_URL_MAPPING_JSON)) {
   if (history_service_) {
     history_service_observation_.Observe(history_service_);
   }
diff --git a/chrome/browser/cart/cart_service.h b/chrome/browser/cart/cart_service.h
index 51b76b52..88e546aa 100644
--- a/chrome/browser/cart/cart_service.h
+++ b/chrome/browser/cart/cart_service.h
@@ -7,6 +7,7 @@
 #include "base/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/values.h"
 #include "chrome/browser/cart/cart_db.h"
 #include "chrome/browser/cart/cart_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -78,6 +79,7 @@
 
  private:
   friend class CartServiceFactory;
+  friend class CartServiceTest;
 
   // Use |CartServiceFactory::GetForProfile(...)| to get an instance of this
   // service.
@@ -120,6 +122,8 @@
   history::HistoryService* history_service_;
   base::ScopedObservation<history::HistoryService, HistoryServiceObserver>
       history_service_observation_{this};
+  base::Optional<base::Value> domain_name_mapping_;
+  base::Optional<base::Value> domain_cart_url_mapping_;
   base::WeakPtrFactory<CartService> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index 0dd8db1c..a169e78 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -132,6 +132,21 @@
     std::move(closure).Run();
   }
 
+  std::string getDomainName(base::StringPiece domain) {
+    std::string* res = service_->domain_name_mapping_->FindStringKey(domain);
+    if (!res)
+      return "";
+    return *res;
+  }
+
+  std::string getDomainCartURL(base::StringPiece domain) {
+    std::string* res =
+        service_->domain_cart_url_mapping_->FindStringKey(domain);
+    if (!res)
+      return "";
+    return *res;
+  }
+
   void TearDown() override {}
 
  protected:
@@ -651,3 +666,22 @@
                      run_loop[2].QuitClosure(), result3));
   run_loop[2].Run();
 }
+
+// Tests domain to merchant name mapping.
+TEST_F(CartServiceTest, TestDomainToNameMapping) {
+  EXPECT_EQ("Amazon", getDomainName("amazon.com"));
+
+  EXPECT_EQ("eBay", getDomainName("ebay.com"));
+
+  EXPECT_EQ("", getDomainName("example.com"));
+}
+
+// Tests domain to cart URL mapping.
+TEST_F(CartServiceTest, TestDomainToCartURLMapping) {
+  EXPECT_EQ("https://www.amazon.com/gp/cart/view.html?ref_=nav_cart",
+            getDomainCartURL("amazon.com"));
+
+  EXPECT_EQ("https://cart.ebay.com", getDomainCartURL("ebay.com"));
+
+  EXPECT_EQ("", getDomainCartURL("example.com"));
+}
diff --git a/chrome/browser/cart/resources/cart_domain_cart_url_mapping.json b/chrome/browser/cart/resources/cart_domain_cart_url_mapping.json
new file mode 100644
index 0000000..c5109d5
--- /dev/null
+++ b/chrome/browser/cart/resources/cart_domain_cart_url_mapping.json
@@ -0,0 +1,5 @@
+{
+"amazon.com": "https://www.amazon.com/gp/cart/view.html?ref_=nav_cart",
+"ebay.com": "https://cart.ebay.com",
+"walmart.com": "https://www.walmart.com/cart"
+}
\ No newline at end of file
diff --git a/chrome/browser/cart/resources/cart_domain_name_mapping.json b/chrome/browser/cart/resources/cart_domain_name_mapping.json
new file mode 100644
index 0000000..5599c17
--- /dev/null
+++ b/chrome/browser/cart/resources/cart_domain_name_mapping.json
@@ -0,0 +1,5 @@
+{
+"amazon.com": "Amazon",
+"ebay.com": "eBay",
+"walmart.com": "Walmart"
+}
diff --git a/chrome/browser/cart/resources/cart_resources.grdp b/chrome/browser/cart/resources/cart_resources.grdp
new file mode 100644
index 0000000..91ae25f3
--- /dev/null
+++ b/chrome/browser/cart/resources/cart_resources.grdp
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <include name="IDR_CART_DOMAIN_NAME_MAPPING_JSON"
+           file="cart/resources/cart_domain_name_mapping.json" type="BINDATA" />
+  <include name="IDR_CART_DOMAIN_CART_URL_MAPPING_JSON"
+          file="cart/resources/cart_domain_cart_url_mapping.json" type="BINDATA" />
+</grit-part>
\ No newline at end of file
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 277a7349..9ff63d2 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -111,7 +111,11 @@
 #include "chrome/browser/speech/speech_recognition_client_browser_interface.h"
 #include "chrome/browser/speech/speech_recognition_client_browser_interface_factory.h"
 #include "chrome/browser/speech/speech_recognition_service.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
+#else
 #include "chrome/browser/speech/speech_recognition_service_factory.h"
+#endif
 #include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
 #include "chrome/browser/ui/webui/downloads/downloads_ui.h"
 #include "chrome/browser/ui/webui/realbox/realbox.mojom.h"
@@ -423,8 +427,13 @@
   Profile* profile = Profile::FromBrowserContext(
       frame_host->GetProcess()->GetBrowserContext());
   if (base::FeatureList::IsEnabled(media::kLiveCaption)) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    CrosSpeechRecognitionServiceFactory::GetForProfile(profile)->Create(
+        std::move(receiver));
+#else
     SpeechRecognitionServiceFactory::GetForProfile(profile)->Create(
         std::move(receiver));
+#endif
   }
 }
 
@@ -770,11 +779,11 @@
 
   RegisterWebUIControllerInterfaceBinder<
       chromeos::diagnostics::mojom::SystemDataProvider,
-      chromeos::DiagnosticsUI>(map);
+      chromeos::DiagnosticsDialogUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
       chromeos::diagnostics::mojom::SystemRoutineController,
-      chromeos::DiagnosticsUI>(map);
+      chromeos::DiagnosticsDialogUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<chromeos::scanning::mojom::ScanService,
                                          chromeos::ScanningUI>(map);
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 93de272..ea65786 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -446,7 +446,8 @@
   base::FilePath user_data_dir =
       g_browser_process->profile_manager()->user_data_dir();
   base::FilePath startup_profile_dir =
-      GetStartupProfilePath(user_data_dir, current_directory, command_line);
+      GetStartupProfilePath(user_data_dir, current_directory, command_line,
+                            /*ignore_profile_picker=*/false);
 
   StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
       command_line, current_directory, startup_profile_dir);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6783ddf3..16196723 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -413,6 +413,7 @@
 #include "ash/public/cpp/tablet_mode.h"
 #include "chrome/app/chrome_crash_reporter_client.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_backend_delegate.h"
@@ -434,7 +435,6 @@
 #include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h"
 #include "chrome/browser/chromeos/policy/system_proxy_manager.h"
 #include "chrome/browser/chromeos/smb_client/fileapi/smbfs_file_system_backend_delegate.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/speech/tts_chromeos.h"
 #include "chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h"
 #include "chrome/browser/ui/browser_dialogs.h"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 2801656b..17a6363 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -547,6 +547,34 @@
     "../ash/profiles/profile_helper.cc",
     "../ash/profiles/profile_helper.h",
     "../ash/reset/metrics.h",
+    "../ash/system/automatic_reboot_manager.cc",
+    "../ash/system/automatic_reboot_manager.h",
+    "../ash/system/automatic_reboot_manager_observer.h",
+    "../ash/system/breakpad_consent_watcher.cc",
+    "../ash/system/breakpad_consent_watcher.h",
+    "../ash/system/device_disabling_manager.cc",
+    "../ash/system/device_disabling_manager.h",
+    "../ash/system/device_disabling_manager_default_delegate.cc",
+    "../ash/system/device_disabling_manager_default_delegate.h",
+    "../ash/system/fake_input_device_settings.cc",
+    "../ash/system/fake_input_device_settings.h",
+    "../ash/system/input_device_settings.cc",
+    "../ash/system/input_device_settings.h",
+    "../ash/system/input_device_settings_impl_ozone.cc",
+    "../ash/system/pointer_device_observer.cc",
+    "../ash/system/pointer_device_observer.h",
+    "../ash/system/procfs_util.cc",
+    "../ash/system/procfs_util.h",
+    "../ash/system/system_clock.cc",
+    "../ash/system/system_clock.h",
+    "../ash/system/system_clock_observer.cc",
+    "../ash/system/system_clock_observer.h",
+    "../ash/system/timezone_resolver_manager.cc",
+    "../ash/system/timezone_resolver_manager.h",
+    "../ash/system/timezone_util.cc",
+    "../ash/system/timezone_util.h",
+    "../ash/system/user_removal_manager.cc",
+    "../ash/system/user_removal_manager.h",
     "android_sms/android_sms_app_manager.cc",
     "android_sms/android_sms_app_manager.h",
     "android_sms/android_sms_app_manager_impl.cc",
@@ -2832,34 +2860,6 @@
     "sync/split_settings_sync_field_trial.h",
     "sync/turn_sync_on_helper.cc",
     "sync/turn_sync_on_helper.h",
-    "system/automatic_reboot_manager.cc",
-    "system/automatic_reboot_manager.h",
-    "system/automatic_reboot_manager_observer.h",
-    "system/breakpad_consent_watcher.cc",
-    "system/breakpad_consent_watcher.h",
-    "system/device_disabling_manager.cc",
-    "system/device_disabling_manager.h",
-    "system/device_disabling_manager_default_delegate.cc",
-    "system/device_disabling_manager_default_delegate.h",
-    "system/fake_input_device_settings.cc",
-    "system/fake_input_device_settings.h",
-    "system/input_device_settings.cc",
-    "system/input_device_settings.h",
-    "system/input_device_settings_impl_ozone.cc",
-    "system/pointer_device_observer.cc",
-    "system/pointer_device_observer.h",
-    "system/procfs_util.cc",
-    "system/procfs_util.h",
-    "system/system_clock.cc",
-    "system/system_clock.h",
-    "system/system_clock_observer.cc",
-    "system/system_clock_observer.h",
-    "system/timezone_resolver_manager.cc",
-    "system/timezone_resolver_manager.h",
-    "system/timezone_util.cc",
-    "system/timezone_util.h",
-    "system/user_removal_manager.cc",
-    "system/user_removal_manager.h",
     "system_logs/command_line_log_source.cc",
     "system_logs/command_line_log_source.h",
     "system_logs/crosapi_system_log_source.cc",
@@ -3437,6 +3437,10 @@
     "../ash/app_mode/web_app/web_kiosk_app_launcher_unittest.cc",
     "../ash/assistant/assistant_util_unittest.cc",
     "../ash/mobile/mobile_activator_unittest.cc",
+    "../ash/system/automatic_reboot_manager_unittest.cc",
+    "../ash/system/device_disabling_manager_unittest.cc",
+    "../ash/system/procfs_util_unittest.cc",
+    "../ash/system/user_removal_manager_unittest.cc",
     "../download/notification/download_item_notification_unittest.cc",
     "../extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc",
     "../extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc",
@@ -3549,6 +3553,7 @@
     "bluetooth/debug_logs_manager_unittest.cc",
     "borealis/borealis_app_launcher_unittest.cc",
     "borealis/borealis_context_manager_unittest.cc",
+    "borealis/borealis_context_unittest.cc",
     "borealis/borealis_features_unittest.cc",
     "borealis/borealis_installer_unittest.cc",
     "borealis/borealis_launch_watcher_unittest.cc",
@@ -4052,10 +4057,6 @@
     "startup_settings_cache_unittest.cc",
     "sync/os_sync_util_unittest.cc",
     "sync/turn_sync_on_helper_unittest.cc",
-    "system/automatic_reboot_manager_unittest.cc",
-    "system/device_disabling_manager_unittest.cc",
-    "system/procfs_util_unittest.cc",
-    "system/user_removal_manager_unittest.cc",
     "system_logs/crosapi_system_log_source_unittest.cc",
     "system_logs/shill_log_source_unittest.cc",
     "system_logs/single_debug_daemon_log_source_unittest.cc",
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
index af63d8b..75b91f5b 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
@@ -20,7 +20,6 @@
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "ui/display/types/display_constants.h"
 
 namespace chromeos {
 
@@ -54,8 +53,7 @@
       apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
                           WindowOpenDisposition::NEW_WINDOW,
                           false /* preferred_containner */),
-      apps::mojom::LaunchSource::kFromChromeInternal,
-      display::kInvalidDisplayId);
+      apps::mojom::LaunchSource::kFromChromeInternal);
 }
 
 bool AndroidSmsAppManagerImpl::PwaDelegate::TransferItemAttributes(
diff --git a/chrome/browser/chromeos/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/chromeos/apps/intent_helper/chromeos_intent_picker_helpers.cc
index 6de9731f..5d756b9 100644
--- a/chrome/browser/chromeos/apps/intent_helper/chromeos_intent_picker_helpers.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -186,7 +186,7 @@
           GetEventFlags(mojom::LaunchContainer::kLaunchContainerWindow,
                         WindowOpenDisposition::NEW_WINDOW,
                         /*prefer_container=*/true),
-          url, launch_source, display::kDefaultDisplayId);
+          url, launch_source, apps::MakeWindowInfo(display::kDefaultDisplayId));
       CloseOrGoBack(web_contents);
     }
   }
diff --git a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
index bd6d596..60cb505 100644
--- a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -86,7 +86,8 @@
             GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
                           WindowOpenDisposition::NEW_WINDOW,
                           /*prefer_container=*/true),
-            url, launch_source, display::kDefaultDisplayId);
+            url, launch_source,
+            apps::MakeWindowInfo(display::kDefaultDisplayId));
         CloseOrGoBack(web_contents);
         IntentHandlingMetrics::RecordIntentPickerUserInteractionMetrics(
             /*selected_app_package=*/preferred_app_id.value(),
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
index 93deaa9..e56729e3 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
@@ -16,12 +16,12 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
diff --git a/chrome/browser/chromeos/borealis/borealis_context.cc b/chrome/browser/chromeos/borealis/borealis_context.cc
index aee2e1d2..dfe0be6e 100644
--- a/chrome/browser/chromeos/borealis/borealis_context.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context.cc
@@ -6,9 +6,11 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/scoped_observation.h"
+#include "chrome/browser/chromeos/borealis/borealis_metrics.h"
 #include "chrome/browser/chromeos/borealis/borealis_service.h"
 #include "chrome/browser/chromeos/borealis/borealis_shutdown_monitor.h"
 #include "chrome/browser/chromeos/borealis/borealis_window_manager.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_stability_monitor.h"
 
 namespace borealis {
 
@@ -46,9 +48,16 @@
 
 BorealisContext::~BorealisContext() = default;
 
+void BorealisContext::NotifyUnexpectedVmShutdown() {
+  guest_os_stability_monitor_->LogUnexpectedVmShutdown();
+}
+
 BorealisContext::BorealisContext(Profile* profile)
     : profile_(profile),
-      lifetime_observer_(std::make_unique<BorealisLifetimeObserver>(profile)) {}
+      lifetime_observer_(std::make_unique<BorealisLifetimeObserver>(profile)),
+      guest_os_stability_monitor_(
+          std::make_unique<guest_os::GuestOsStabilityMonitor>(
+              kBorealisStabilityHistogram)) {}
 
 std::unique_ptr<BorealisContext>
 BorealisContext::CreateBorealisContextForTesting(Profile* profile) {
diff --git a/chrome/browser/chromeos/borealis/borealis_context.h b/chrome/browser/chromeos/borealis/borealis_context.h
index 712d0f2..cf0492d 100644
--- a/chrome/browser/chromeos/borealis/borealis_context.h
+++ b/chrome/browser/chromeos/borealis/borealis_context.h
@@ -12,6 +12,9 @@
 
 class Profile;
 
+namespace guest_os {
+class GuestOsStabilityMonitor;
+}
 namespace borealis {
 
 class BorealisLifetimeObserver;
@@ -41,6 +44,10 @@
   const base::FilePath& disk_path() const { return disk_path_; }
   void set_disk_path(base::FilePath path) { disk_path_ = std::move(path); }
 
+  // Called to signal that this Borealis VM is being unexpectedly shut down.
+  // Not to be called during intentional shutdowns.
+  void NotifyUnexpectedVmShutdown();
+
  private:
   friend class BorealisContextManagerImpl;
 
@@ -53,6 +60,9 @@
   // This instance listens for the session to finish and issues an automatic
   // shutdown when it does.
   std::unique_ptr<BorealisLifetimeObserver> lifetime_observer_;
+
+  std::unique_ptr<guest_os::GuestOsStabilityMonitor>
+      guest_os_stability_monitor_;
 };
 
 }  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
index af624fd..d9182570 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
@@ -81,9 +81,24 @@
 }
 
 BorealisContextManagerImpl::BorealisContextManagerImpl(Profile* profile)
-    : profile_(profile), weak_factory_(this) {}
+    : profile_(profile), weak_factory_(this) {
+  // DBusThreadManager may not be initialized in tests.
+  if (chromeos::DBusThreadManager::IsInitialized()) {
+    chromeos::DBusThreadManager::Get()->GetConciergeClient()->AddVmObserver(
+        this);
+  }
+}
 
-BorealisContextManagerImpl::~BorealisContextManagerImpl() = default;
+BorealisContextManagerImpl::~BorealisContextManagerImpl() {
+  // Even if initialized, DBusThreadManager may be destroyed prior to
+  // BorealisService/BorealisContextManagerImpl in tests. Therefore we must not
+  // keep a pointer to the observed ConciergeClient, either directly or via
+  // ScopedObservation or similar.
+  if (chromeos::DBusThreadManager::IsInitialized()) {
+    chromeos::DBusThreadManager::Get()->GetConciergeClient()->RemoveVmObserver(
+        this);
+  }
+}
 
 void BorealisContextManagerImpl::StartBorealis(ResultCallback callback) {
   if (context_) {
@@ -199,4 +214,23 @@
   }
 }
 
+// TODO(b/179620544): Move handling of unexpected shutdowns to
+// BorealisLaunchWatcher.
+void BorealisContextManagerImpl::OnVmStarted(
+    const vm_tools::concierge::VmStartedSignal& signal) {}
+
+void BorealisContextManagerImpl::OnVmStopped(
+    const vm_tools::concierge::VmStoppedSignal& signal) {
+  if (context_ && context_->vm_name() == signal.name() &&
+      signal.owner_id() ==
+          chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_)) {
+    // If |context_| exists, it's a "running" Borealis instance which we didn't
+    // request to shut down.
+    context_->NotifyUnexpectedVmShutdown();
+
+    // Update our state to reflect the unexpected VM exit.
+    context_.reset();
+  }
+}
+
 }  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
index b4a3d6e..b2b7a7e 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/borealis/infra/described.h"
 #include "chrome/browser/chromeos/borealis/infra/transition.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/dbus/concierge_client.h"
 
 namespace borealis {
 
@@ -21,7 +22,9 @@
 
 // The Borealis Context Manager is a keyed service responsible for managing
 // the Borealis VM startup flow and guaranteeing its state to other processes.
-class BorealisContextManagerImpl : public BorealisContextManager {
+class BorealisContextManagerImpl
+    : public BorealisContextManager,
+      public chromeos::ConciergeClient::VmObserver {
  public:
   explicit BorealisContextManagerImpl(Profile* profile);
   BorealisContextManagerImpl(const BorealisContextManagerImpl&) = delete;
@@ -83,7 +86,12 @@
   BorealisContextManager::ContextOrFailure GetResult(
       const Startup::Result& completion_result);
 
+  // chromeos::ConciergeClient::VmObserver:
+  void OnVmStarted(const vm_tools::concierge::VmStartedSignal& signal) override;
+  void OnVmStopped(const vm_tools::concierge::VmStoppedSignal& signal) override;
+
   Profile* const profile_;
+
   std::unique_ptr<Startup> in_progress_startup_;
   std::unique_ptr<BorealisContext> context_;
   base::queue<ResultCallback> callback_queue_;
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc b/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
index 3c307f51..2dc62ac4 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
@@ -11,9 +11,11 @@
 #include "base/containers/queue.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/borealis/borealis_context_manager.h"
 #include "chrome/browser/chromeos/borealis/borealis_metrics.h"
 #include "chrome/browser/chromeos/borealis/borealis_task.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_stability_monitor.h"
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -108,6 +110,17 @@
     histogram_tester_.reset();
   }
 
+  void SendVmStoppedSignal() {
+    auto* concierge_client = static_cast<chromeos::FakeConciergeClient*>(
+        chromeos::DBusThreadManager::Get()->GetConciergeClient());
+
+    vm_tools::concierge::VmStoppedSignal signal;
+    signal.set_name("test_vm_name");
+    signal.set_owner_id(
+        ash::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
+    concierge_client->NotifyVmStopped(signal);
+  }
+
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<base::HistogramTester> histogram_tester_;
@@ -336,5 +349,59 @@
   task_environment_.RunUntilIdle();
 }
 
+TEST_F(BorealisContextManagerTest, ShouldNotLogVmStoppedWhenNotRunning) {
+  SendVmStoppedSignal();
+  histogram_tester_->ExpectUniqueSample(kBorealisStabilityHistogram,
+                                        guest_os::FailureClasses::VmStopped, 0);
+}
+
+TEST_F(BorealisContextManagerTest, ShouldNotLogVmStoppedDuringStartup) {
+  testing::StrictMock<ResultCallbackHandler> callback_expectation;
+
+  BorealisContextManagerImplForTesting context_manager(
+      profile_.get(), /*tasks=*/1, /*success=*/true);
+  context_manager.StartBorealis(callback_expectation.GetCallback());
+
+  SendVmStoppedSignal();
+
+  histogram_tester_->ExpectUniqueSample(kBorealisStabilityHistogram,
+                                        guest_os::FailureClasses::VmStopped, 0);
+}
+
+TEST_F(BorealisContextManagerTest, ShouldNotLogVmStoppedWhenExpected) {
+  testing::StrictMock<ResultCallbackHandler> callback_expectation;
+  // No need to verify the shutdown callback for this test case.
+  ShutdownCallbackHandler shutdown_callback;
+
+  BorealisContextManagerImplForTesting context_manager(
+      profile_.get(), /*tasks=*/1, /*success=*/true);
+  EXPECT_CALL(callback_expectation, Callback(IsSuccessResult()));
+  context_manager.StartBorealis(callback_expectation.GetCallback());
+  task_environment_.RunUntilIdle();
+
+  context_manager.ShutDownBorealis(shutdown_callback.GetCallback());
+  SendVmStoppedSignal();
+
+  histogram_tester_->ExpectUniqueSample(kBorealisStabilityHistogram,
+                                        guest_os::FailureClasses::VmStopped, 0);
+
+  // Wait for shutdown to complete before destructing |shutdown_callback|.
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisContextManagerTest, LogVmStoppedWhenUnexpected) {
+  testing::StrictMock<ResultCallbackHandler> callback_expectation;
+
+  BorealisContextManagerImplForTesting context_manager(
+      profile_.get(), /*tasks=*/1, /*success=*/true);
+  EXPECT_CALL(callback_expectation, Callback(IsSuccessResult()));
+  context_manager.StartBorealis(callback_expectation.GetCallback());
+  task_environment_.RunUntilIdle();
+
+  SendVmStoppedSignal();
+  histogram_tester_->ExpectUniqueSample(kBorealisStabilityHistogram,
+                                        guest_os::FailureClasses::VmStopped, 1);
+}
+
 }  // namespace
 }  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_context_unittest.cc b/chrome/browser/chromeos/borealis/borealis_context_unittest.cc
new file mode 100644
index 0000000..dd879e837
--- /dev/null
+++ b/chrome/browser/chromeos/borealis/borealis_context_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright 2020 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/guest_os/guest_os_stability_monitor.h"
+
+#include <memory>
+
+#include "base/barrier_closure.h"
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/chromeos/borealis/borealis_context.h"
+#include "chrome/browser/chromeos/borealis/borealis_metrics.h"
+#include "chrome/browser/chromeos/borealis/borealis_service_fake.h"
+#include "chrome/browser/chromeos/borealis/borealis_shutdown_monitor.h"
+#include "chrome/browser/chromeos/borealis/borealis_window_manager.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_chunneld_client.h"
+#include "chromeos/dbus/fake_cicerone_client.h"
+#include "chromeos/dbus/fake_concierge_client.h"
+#include "chromeos/dbus/fake_seneschal_client.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace borealis {
+
+class BorealisContextTest : public testing::Test {
+ public:
+  BorealisContextTest() {
+    chromeos::DBusThreadManager::Initialize();
+
+    profile_ = std::make_unique<TestingProfile>();
+    borealis_shutdown_monitor_ =
+        std::make_unique<BorealisShutdownMonitor>(profile_.get());
+    borealis_window_manager =
+        std::make_unique<BorealisWindowManager>(profile_.get());
+
+    service_fake_ = BorealisServiceFake::UseFakeForTesting(profile_.get());
+    service_fake_->SetShutdownMonitorForTesting(
+        borealis_shutdown_monitor_.get());
+    service_fake_->SetWindowManagerForTesting(borealis_window_manager.get());
+
+    borealis_context_ =
+        BorealisContext::CreateBorealisContextForTesting(profile_.get());
+
+    // When GuestOsStabilityMonitor is initialized, it waits for the DBus
+    // services to become available before monitoring them. In tests this
+    // happens instantly, but the notification still comes via a callback on the
+    // task queue, so run all queued tasks here.
+    FlushTaskQueue();
+
+    histogram_tester_.ExpectTotalCount(kBorealisStabilityHistogram, 0);
+  }
+
+  ~BorealisContextTest() override {
+    borealis_context_.reset();  // must destroy before DBusThreadManager
+    chromeos::DBusThreadManager::Shutdown();
+  }
+
+  // Run all tasks queued prior to this method being called, but not any tasks
+  // that are scheduled as a result of those tasks running. This is done by
+  // placing a quit closure at the current end of the queue and running until we
+  // hit it.
+  void FlushTaskQueue() {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_env_;
+  std::unique_ptr<borealis::BorealisContext> borealis_context_;
+  std::unique_ptr<TestingProfile> profile_;
+  BorealisServiceFake* service_fake_;
+  std::unique_ptr<BorealisShutdownMonitor> borealis_shutdown_monitor_;
+  std::unique_ptr<BorealisWindowManager> borealis_window_manager;
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_F(BorealisContextTest, ConciergeFailure) {
+  auto* concierge_client = static_cast<chromeos::FakeConciergeClient*>(
+      chromeos::DBusThreadManager::Get()->GetConciergeClient());
+
+  concierge_client->NotifyConciergeStopped();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::ConciergeStopped,
+      1);
+
+  concierge_client->NotifyConciergeStarted();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::ConciergeStopped,
+      1);
+}
+
+TEST_F(BorealisContextTest, CiceroneFailure) {
+  auto* cicerone_client = static_cast<chromeos::FakeCiceroneClient*>(
+      chromeos::DBusThreadManager::Get()->GetCiceroneClient());
+
+  cicerone_client->NotifyCiceroneStopped();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::CiceroneStopped,
+      1);
+
+  cicerone_client->NotifyCiceroneStarted();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::CiceroneStopped,
+      1);
+}
+
+TEST_F(BorealisContextTest, SeneschalFailure) {
+  auto* seneschal_client = static_cast<chromeos::FakeSeneschalClient*>(
+      chromeos::DBusThreadManager::Get()->GetSeneschalClient());
+
+  seneschal_client->NotifySeneschalStopped();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::SeneschalStopped,
+      1);
+
+  seneschal_client->NotifySeneschalStarted();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::SeneschalStopped,
+      1);
+}
+
+TEST_F(BorealisContextTest, ChunneldFailure) {
+  auto* chunneld_client = static_cast<chromeos::FakeChunneldClient*>(
+      chromeos::DBusThreadManager::Get()->GetChunneldClient());
+
+  chunneld_client->NotifyChunneldStopped();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::ChunneldStopped,
+      1);
+
+  chunneld_client->NotifyChunneldStarted();
+  histogram_tester_.ExpectUniqueSample(
+      kBorealisStabilityHistogram, guest_os::FailureClasses::ChunneldStopped,
+      1);
+}
+
+}  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_metrics.cc b/chrome/browser/chromeos/borealis/borealis_metrics.cc
index d50b2b1..2a0730b 100644
--- a/chrome/browser/chromeos/borealis/borealis_metrics.cc
+++ b/chrome/browser/chromeos/borealis/borealis_metrics.cc
@@ -13,6 +13,7 @@
 const char kBorealisInstallResultHistogram[] = "Borealis.Install.Result";
 const char kBorealisInstallOverallTimeHistogram[] =
     "Borealis.Install.OverallTime";
+const char kBorealisStabilityHistogram[] = "Borealis.Stability";
 const char kBorealisStartupNumAttemptsHistogram[] =
     "Borealis.Startup.NumAttempts";
 const char kBorealisStartupResultHistogram[] = "Borealis.Startup.Result";
diff --git a/chrome/browser/chromeos/borealis/borealis_metrics.h b/chrome/browser/chromeos/borealis/borealis_metrics.h
index dc99f70..9ccfa21 100644
--- a/chrome/browser/chromeos/borealis/borealis_metrics.h
+++ b/chrome/browser/chromeos/borealis/borealis_metrics.h
@@ -12,6 +12,7 @@
 extern const char kBorealisInstallNumAttemptsHistogram[];
 extern const char kBorealisInstallResultHistogram[];
 extern const char kBorealisInstallOverallTimeHistogram[];
+extern const char kBorealisStabilityHistogram[];
 extern const char kBorealisStartupNumAttemptsHistogram[];
 extern const char kBorealisStartupResultHistogram[];
 extern const char kBorealisStartupOverallTimeHistogram[];
diff --git a/chrome/browser/chromeos/borealis/borealis_service_fake.cc b/chrome/browser/chromeos/borealis/borealis_service_fake.cc
index 8b3d4f0..e7f31b68 100644
--- a/chrome/browser/chromeos/borealis/borealis_service_fake.cc
+++ b/chrome/browser/chromeos/borealis/borealis_service_fake.cc
@@ -23,37 +23,37 @@
 BorealisServiceFake::~BorealisServiceFake() = default;
 
 BorealisAppLauncher& BorealisServiceFake::AppLauncher() {
-  DCHECK(app_launcher_);
+  CHECK(app_launcher_);
   return *app_launcher_;
 }
 
 BorealisAppUninstaller& BorealisServiceFake::AppUninstaller() {
-  DCHECK(app_uninstaller_);
+  CHECK(app_uninstaller_);
   return *app_uninstaller_;
 }
 
 BorealisContextManager& BorealisServiceFake::ContextManager() {
-  DCHECK(context_manager_);
+  CHECK(context_manager_);
   return *context_manager_;
 }
 
 BorealisFeatures& BorealisServiceFake::Features() {
-  DCHECK(features_);
+  CHECK(features_);
   return *features_;
 }
 
 BorealisInstaller& BorealisServiceFake::Installer() {
-  DCHECK(installer_);
+  CHECK(installer_);
   return *installer_;
 }
 
 BorealisShutdownMonitor& BorealisServiceFake::ShutdownMonitor() {
-  DCHECK(shutdown_monitor_);
+  CHECK(shutdown_monitor_);
   return *shutdown_monitor_;
 }
 
 BorealisWindowManager& BorealisServiceFake::WindowManager() {
-  DCHECK(window_manager_);
+  CHECK(window_manager_);
   return *window_manager_;
 }
 
diff --git a/chrome/browser/chromeos/borealis/borealis_task_unittest.cc b/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
index da532571..ff71164 100644
--- a/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
+++ b/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
@@ -62,6 +62,7 @@
 
   void TearDown() override {
     profile_.reset();
+    context_.reset();  // must destroy before DBus shutdown
 
     chromeos::DlcserviceClient::Shutdown();
     chromeos::DBusThreadManager::Shutdown();
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc b/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
index 7a542ec9..fe193c26 100644
--- a/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
@@ -13,6 +13,7 @@
 #include "base/optional.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
 #include "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -97,7 +98,7 @@
 void AppServiceWrapper::LaunchApp(const std::string& app_service_id) {
   GetAppProxy()->Launch(app_service_id, ui::EventFlags::EF_NONE,
                         apps::mojom::LaunchSource::kFromParentalControls,
-                        display::kDefaultDisplayId);
+                        apps::MakeWindowInfo(display::kDefaultDisplayId));
 }
 
 std::vector<AppId> AppServiceWrapper::GetInstalledApps() const {
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 0168a047..3c12f50e 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -42,6 +42,9 @@
 #include "chrome/browser/ash/app_mode/kiosk_mode_idle_app_name_notification.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/breakpad_consent_watcher.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ash/system/user_removal_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -119,9 +122,6 @@
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/chromeos/settings/shutdown_policy_forwarder.h"
 #include "chrome/browser/chromeos/startup_settings_cache.h"
-#include "chrome/browser/chromeos/system/breakpad_consent_watcher.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/chromeos/system/user_removal_manager.h"
 #include "chrome/browser/chromeos/system_token_cert_db_initializer.h"
 #include "chrome/browser/chromeos/ui/gnubby_notification.h"
 #include "chrome/browser/chromeos/ui/low_disk_notification.h"
diff --git a/chrome/browser/chromeos/crostini/crosvm_metrics.h b/chrome/browser/chromeos/crostini/crosvm_metrics.h
index b6d98d8..d428398 100644
--- a/chrome/browser/chromeos/crostini/crosvm_metrics.h
+++ b/chrome/browser/chromeos/crostini/crosvm_metrics.h
@@ -14,7 +14,7 @@
 #include "base/optional.h"
 #include "base/sequenced_task_runner.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/chromeos/system/procfs_util.h"
+#include "chrome/browser/ash/system/procfs_util.h"
 
 namespace crostini {
 
diff --git a/chrome/browser/chromeos/crostini/crosvm_process_list.h b/chrome/browser/chromeos/crostini/crosvm_process_list.h
index 708629f9..1040ecdc 100644
--- a/chrome/browser/chromeos/crostini/crosvm_process_list.h
+++ b/chrome/browser/chromeos/crostini/crosvm_process_list.h
@@ -9,7 +9,7 @@
 #include <unordered_map>
 
 #include "base/files/file_path.h"
-#include "chrome/browser/chromeos/system/procfs_util.h"
+#include "chrome/browser/ash/system/procfs_util.h"
 
 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 558a1b4d..0d5461b 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -59,6 +59,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/assistant/assistant_util.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -84,7 +85,6 @@
 #include "chrome/browser/chromeos/printing/cups_printers_manager_factory.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/chrome_policy_conversions_client.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
index 6ff5287c..054dc8ab 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
@@ -336,7 +336,7 @@
   // Grant access to this particular file to target extension. This will
   // ensure that the target extension can access only this FS entry and
   // prevent from traversing FS hierarchy upward.
-  external_backend->GrantFileAccessToExtension(extension_id(),
+  external_backend->GrantFileAccessToExtension(extension_id_or_file_app_id(),
                                                file_definition.virtual_path);
 
   // Grant access to the selected file to target extensions render view process.
@@ -344,7 +344,8 @@
       render_frame_host()->GetProcess()->GetID(), full_path);
 
   file_manager::util::ConvertFileDefinitionToEntryDefinition(
-      chrome_details.GetProfile(), extension_id(), file_definition,
+      chrome_details.GetProfile(), extension_id_or_file_app_id(),
+      file_definition,
       base::BindOnce(
           &FileBrowserHandlerInternalSelectFileFunction::RespondEntryDefinition,
           this));
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index 2b18be2f7..6b46b1a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -446,8 +446,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Permissions) {
-  EXPECT_TRUE(
-      RunExtensionTestIgnoreManifestWarnings("file_browser/permissions"));
+  EXPECT_TRUE(RunExtensionTest({.name = "file_browser/permissions"},
+                               {.ignore_manifest_warnings = true}));
   const extensions::Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
   ASSERT_EQ(1u, extension->install_warnings().size());
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_base.h b/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
index 28423ff3..be25965a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
@@ -8,6 +8,7 @@
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_BASE_H_
 
 #include "base/time/time.h"
+#include "chrome/browser/chromeos/extensions/file_manager/files_extension_function.h"
 #include "extensions/browser/extension_function.h"
 
 namespace extensions {
@@ -20,7 +21,7 @@
 // set_log_on_completion(true) to enable it, if they want. However, even if
 // the logging is turned off, a warning is emitted when a function call is
 // very slow. See the implementation of OnResponded() for details.
-class LoggedExtensionFunction : public ExtensionFunction {
+class LoggedExtensionFunction : public FilesExtensionFunction {
  public:
   LoggedExtensionFunction();
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index 3363f02b..40f945d42 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -397,7 +397,8 @@
     if (profile->IsOffTheRecord())
       continue;
     storage::FileSystemContext* const context =
-        util::GetStoragePartitionForExtensionId(extension_id(), profile)
+        util::GetStoragePartitionForExtensionId(extension_id_or_file_app_id(),
+                                                profile)
             ->GetFileSystemContext();
     for (const auto& url : params->entry_urls) {
       const storage::FileSystemURL file_system_url =
@@ -408,7 +409,7 @@
           file_system_url.mount_type() != storage::kFileSystemTypeExternal) {
         continue;
       }
-      backend->GrantFileAccessToExtension(extension_id(),
+      backend->GrantFileAccessToExtension(extension_id_or_file_app_id(),
                                           file_system_url.virtual_path());
       content::ChildProcessSecurityPolicy::GetInstance()
           ->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
@@ -525,7 +526,7 @@
           &PostNotificationCallbackTaskToUIThread,
           base::BindRepeating(
               &file_manager::EventRouter::OnWatcherManagerNotification,
-              event_router, file_system_url, extension_id())));
+              event_router, file_system_url, extension_id_or_file_app_id())));
 }
 
 void FileManagerPrivateInternalAddFileWatchFunction::
@@ -537,7 +538,8 @@
 
   // Obsolete. Fallback code if storage::WatcherManager is not implemented.
   event_router->AddFileWatch(
-      file_system_url.path(), file_system_url.virtual_path(), extension_id(),
+      file_system_url.path(), file_system_url.virtual_path(),
+      extension_id_or_file_app_id(),
       base::BindOnce(&FileWatchFunctionBase::RespondWith, this));
 }
 
@@ -566,7 +568,8 @@
   DCHECK(event_router);
 
   // Obsolete. Fallback code if storage::WatcherManager is not implemented.
-  event_router->RemoveFileWatch(file_system_url.path(), extension_id());
+  event_router->RemoveFileWatch(file_system_url.path(),
+                                extension_id_or_file_app_id());
   RespondWith(true);
 }
 
@@ -1062,8 +1065,8 @@
     FileDefinition file_definition;
     const bool result =
         file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
-            chrome_details.GetProfile(), extension_id(), file_system_url.path(),
-            &file_definition.virtual_path);
+            chrome_details.GetProfile(), extension_id_or_file_app_id(),
+            file_system_url.path(), &file_definition.virtual_path);
     if (!result)
       continue;
     // The API only supports isolated files. It still works for directories,
@@ -1073,7 +1076,7 @@
   }
 
   file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
-      chrome_details.GetProfile(), extension_id(),
+      chrome_details.GetProfile(), extension_id_or_file_app_id(),
       file_definition_list,  // Safe, since copied internally.
       base::BindOnce(
           &FileManagerPrivateInternalResolveIsolatedEntriesFunction::
@@ -1302,7 +1305,8 @@
   GURL url;
   base::FilePath my_files_virtual_path;
   if (!file_manager::util::ConvertAbsoluteFilePathToFileSystemUrl(
-          chrome_details_.GetProfile(), my_files_path, extension_id(), &url) ||
+          chrome_details_.GetProfile(), my_files_path,
+          extension_id_or_file_app_id(), &url) ||
       !storage::ExternalMountPoints::GetSystemInstance()->GetVirtualPath(
           my_files_path, &my_files_virtual_path)) {
     Respond(Error("My files is not mounted"));
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
index 0def3ace0..506e5a2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_stream_string_converter.h"
+#include "chrome/browser/chromeos/extensions/file_manager/files_extension_function.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "components/drive/file_errors.h"
@@ -72,7 +73,7 @@
 
 // Grants R/W permissions to profile-specific directories (Drive, Downloads)
 // from other profiles.
-class FileManagerPrivateGrantAccessFunction : public ExtensionFunction {
+class FileManagerPrivateGrantAccessFunction : public FilesExtensionFunction {
  public:
   FileManagerPrivateGrantAccessFunction();
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index 727c235..c0bc776 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -801,11 +801,11 @@
       continue;
     }
     auto entry = std::make_unique<base::DictionaryValue>();
-    entry->SetString(
-        "fileSystemRoot",
-        storage::GetExternalFileSystemRootURIString(
-            extensions::Extension::GetBaseURLFromExtensionId(extension_id()),
-            mount_name));
+    entry->SetString("fileSystemRoot",
+                     storage::GetExternalFileSystemRootURIString(
+                         extensions::Extension::GetBaseURLFromExtensionId(
+                             extension_id_or_file_app_id()),
+                         mount_name));
     entry->SetString("fileSystemName", file_system_name);
     entry->SetString("fileFullPath", full_path);
     // All shared paths should be directories.  Even if this is not true,
@@ -1043,7 +1043,8 @@
 
   model->GetRecentFiles(
       file_system_context.get(),
-      Extension::GetBaseURLFromExtensionId(extension_id()), file_type,
+      Extension::GetBaseURLFromExtensionId(extension_id_or_file_app_id()),
+      file_type,
       base::BindOnce(
           &FileManagerPrivateInternalGetRecentFilesFunction::OnGetRecentFiles,
           this, params->restriction));
@@ -1067,14 +1068,14 @@
     // Recent file system only lists regular files, not directories.
     file_definition.is_directory = false;
     if (file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
-            chrome_details_.GetProfile(), extension_id(), file.url().path(),
-            &file_definition.virtual_path)) {
+            chrome_details_.GetProfile(), extension_id_or_file_app_id(),
+            file.url().path(), &file_definition.virtual_path)) {
       file_definition_list.emplace_back(std::move(file_definition));
     }
   }
 
   file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
-      chrome_details_.GetProfile(), extension_id(),
+      chrome_details_.GetProfile(), extension_id_or_file_app_id(),
       file_definition_list,  // Safe, since copied internally.
       base::BindOnce(&FileManagerPrivateInternalGetRecentFilesFunction::
                          OnConvertFileDefinitionListToEntryDefinitionList,
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
index 164d9672..6ac97559 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/files/file.h"
+#include "chrome/browser/chromeos/extensions/file_manager/files_extension_function.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
@@ -355,7 +356,7 @@
 // Implements the chrome.fileManagerPrivate.getCrostiniSharedPaths
 // method.  Returns list of file entries.
 class FileManagerPrivateInternalGetCrostiniSharedPathsFunction
-    : public ExtensionFunction {
+    : public FilesExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileManagerPrivateInternal.getCrostiniSharedPaths",
diff --git a/chrome/browser/chromeos/extensions/info_private_api.cc b/chrome/browser/chromeos/extensions/info_private_api.cc
index 53ba8f8..177906a 100644
--- a/chrome/browser/chromeos/extensions/info_private_api.cc
+++ b/chrome/browser/chromeos/extensions/info_private_api.cc
@@ -19,13 +19,13 @@
 #include "base/values.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/network/device_state.h"
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index 0d42b070..7a8fa2df 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -40,7 +40,6 @@
 #include "content/public/common/content_switches.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
-#include "ui/display/types/display_constants.h"
 #include "ui/events/event_constants.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -54,8 +53,7 @@
       apps::AppServiceProxyFactory::GetForProfile(profile);
 
   proxy->Launch(app_id, ui::EventFlags::EF_NONE,
-                apps::mojom::LaunchSource::kFromChromeInternal,
-                display::kInvalidDisplayId);
+                apps::mojom::LaunchSource::kFromChromeInternal);
   profile->GetPrefs()->SetBoolean(prefs::kFirstRunTutorialShown, true);
 }
 
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 2a381cd..8588dc8 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -62,7 +63,6 @@
 #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
 #include "chrome/browser/chromeos/policy/powerwash_requirements_checker.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 794726e..b10e2079 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -19,6 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/authpolicy/authpolicy_helper.h"
@@ -30,7 +31,6 @@
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h"
 #include "chrome/browser/chromeos/login/user_board_view_mojo.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/login/screens/device_disabled_screen.h b/chrome/browser/chromeos/login/screens/device_disabled_screen.h
index 5326861..4067f84a 100644
--- a/chrome/browser/chromeos/login/screens/device_disabled_screen.h
+++ b/chrome/browser/chromeos/login/screens/device_disabled_screen.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_DEVICE_DISABLED_SCREEN_H_
 
 #include "base/macros.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
 
 namespace chromeos {
 
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index 3dcf4f8..37385a3 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -23,6 +23,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
@@ -39,7 +40,6 @@
 #include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
 #include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.h b/chrome/browser/chromeos/login/screens/user_selection_screen.h
index b04151f..7b945e6 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.h
@@ -16,12 +16,12 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 #include "chrome/browser/chromeos/login/signin/token_handle_util.h"
 #include "chrome/browser/chromeos/login/ui/login_display.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chromeos/components/proximity_auth/screenlock_bridge.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/chromeos/login/screens/welcome_screen.cc b/chrome/browser/chromeos/login/screens/welcome_screen.cc
index d0013d7..d53094c 100644
--- a/chrome/browser/chromeos/login/screens/welcome_screen.cc
+++ b/chrome/browser/chromeos/login/screens/welcome_screen.cc
@@ -17,6 +17,8 @@
 #include "base/time/default_tick_clock.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/base/locale_util.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
@@ -26,8 +28,6 @@
 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/enrollment_requisition_manager.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.cc b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
index 4ba7c97e..f690f3a 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
@@ -7,8 +7,10 @@
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/feature_list.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -25,9 +27,9 @@
 #include "chrome/browser/chromeos/login/ui/webui_accelerator_mapping.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/webui/chromeos/diagnostics_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/locale_switch_screen_handler.h"
@@ -340,6 +342,18 @@
                        weak_factory_.GetWeakPtr()));
     return true;
   }
+
+  if (action == ash::LoginAcceleratorAction::kLaunchDiagnostics &&
+      base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp)) {
+    // Don't handle this action if device is disabled.
+    if (system::DeviceDisablingManager::
+            IsDeviceDisabledDuringNormalOperation()) {
+      return false;
+    }
+    chromeos::DiagnosticsDialog::ShowDialog();
+    return true;
+  }
+
   if (WizardController::default_controller() &&
       WizardController::default_controller()->is_initialized()) {
     if (WizardController::default_controller()->HandleAccelerator(action))
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index d03731f..44b43c5 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -33,6 +33,10 @@
 #include "chrome/browser/ash/app_mode/arc/arc_kiosk_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -55,10 +59,6 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/enrollment_config.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/ash_util.h"
diff --git a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.cc b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.cc
index 763d4c3..eb595262 100644
--- a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.cc
+++ b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/dialog_delegate.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 #include "content/public/browser/browser_context.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace chromeos {
 
@@ -43,6 +44,9 @@
   web_contents()->Focus();
 }
 
+BEGIN_METADATA(WebDialogView, views::WebDialogView)
+END_METADATA
+
 }  // namespace login_screen_extension_ui
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.h b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.h
index 2dc690da..ad44f17 100644
--- a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.h
+++ b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/web_dialog_view.h
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/system_tray_focus_observer.h"
 #include "base/macros.h"
 #include "ui/views/controls/webview/web_dialog_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
 
 namespace content {
@@ -29,11 +30,14 @@
 class WebDialogView : public views::WebDialogView,
                       public ash::SystemTrayFocusObserver {
  public:
+  METADATA_HEADER(WebDialogView);
   explicit WebDialogView(
       content::BrowserContext* context,
       DialogDelegate* delegate,
       std::unique_ptr<ui::WebDialogWebContentsDelegate::WebContentsHandler>
           handler);
+  WebDialogView(const WebDialogView&) = delete;
+  WebDialogView& operator=(const WebDialogView&) = delete;
   ~WebDialogView() override;
 
   // views::WebDialogView
@@ -44,8 +48,6 @@
 
  private:
   DialogDelegate* delegate_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(WebDialogView);
 };
 
 }  // namespace login_screen_extension_ui
diff --git a/chrome/browser/chromeos/login/ui/webui_accelerator_mapping.cc b/chrome/browser/chromeos/login/ui/webui_accelerator_mapping.cc
index 216dd1d..382d71a7 100644
--- a/chrome/browser/chromeos/login/ui/webui_accelerator_mapping.cc
+++ b/chrome/browser/chromeos/login/ui/webui_accelerator_mapping.cc
@@ -26,6 +26,7 @@
 const char kAccelNameAppLaunchNetworkConfig[] = "app_launch_network_config";
 const char kAccelNameDemoMode[] = "demo_mode";
 const char kAccelSendFeedback[] = "send_feedback";
+const char kAccelNameLaunchDiagnostics[] = "launch_diagnostics";
 
 }  // namespace
 
@@ -55,6 +56,8 @@
       return kAccelNameDeviceRequisitionRemora;
     case ash::LoginAcceleratorAction::kStartDemoMode:
       return kAccelNameDemoMode;
+    case ash::LoginAcceleratorAction::kLaunchDiagnostics:
+      return kAccelNameLaunchDiagnostics;
   }
 }
 
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 3f1b0d9d..29a5a33 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -35,6 +35,8 @@
 #include "base/values.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -64,8 +66,6 @@
 #include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
 #include "chrome/browser/chromeos/session_length_limiter.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/permissions_updater.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 9a13bc5..932ce46 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -36,6 +36,9 @@
 #include "chrome/browser/ash/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
+#include "chrome/browser/ash/system/device_disabling_manager.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -105,9 +108,6 @@
 #include "chrome/browser/chromeos/policy/enrollment_requisition_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
-#include "chrome/browser/chromeos/system/device_disabling_manager.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index f91af7c..94e102e 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -23,6 +23,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
 #include "chrome/browser/chromeos/policy/active_directory_policy_manager.h"
 #include "chrome/browser/chromeos/policy/adb_sideloading_allowance_mode_policy_handler.h"
@@ -55,7 +56,6 @@
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/chromeos/ui/adb_sideloading_policy_change_notification.h"
 #include "chrome/browser/policy/device_management_service_configuration.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index c18d0b9..2fe0d799c 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -78,7 +79,6 @@
 #include "chrome/browser/chromeos/policy/device_network_configuration_updater.h"
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h"
@@ -1583,7 +1583,8 @@
                           WindowOpenDisposition::NEW_WINDOW,
                           false /* preferred_containner */),
       apps::mojom::LaunchSource::kFromChromeInternal,
-      display::Screen::GetScreen()->GetPrimaryDisplay().id());
+      apps::MakeWindowInfo(
+          display::Screen::GetScreen()->GetPrimaryDisplay().id()));
   proxy->FlushMojoCallsForTesting();
   run_loop_->Run();
   EXPECT_EQ(1U, app_window_registry->app_windows().size());
diff --git a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
index 8c2322e..ad92f86 100644
--- a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
@@ -10,12 +10,12 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job.cc
index f00c11f..135c061 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/system/user_removal_manager.h"
+#include "chrome/browser/ash/system/user_removal_manager.h"
 #include "components/policy/core/common/remote_commands/remote_commands_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
index b69f8cb..ec47c0f 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
@@ -17,9 +17,9 @@
 #include "base/task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "chrome/browser/ash/system/user_removal_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/remote_commands/device_commands_factory_chromeos.h"
-#include "chrome/browser/chromeos/system/user_removal_manager.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index b86fc5ab..de3d673 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -326,10 +326,6 @@
   }
 }
 
-bool UserCloudPolicyManagerChromeOS::IsClientRegistered() const {
-  return client() && client()->is_registered();
-}
-
 void UserCloudPolicyManagerChromeOS::EnableWildcardLoginCheck(
     const std::string& username) {
   DCHECK(access_token_.empty());
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
index b67ed99..f537039 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
@@ -141,9 +141,6 @@
   // uploading status report) for child user.
   bool RequiresOAuthTokenForChildUser() const;
 
-  // Returns true if the underlying CloudPolicyClient is already registered.
-  bool IsClientRegistered() const;
-
   // Indicates a wildcard login check should be performed once an access token
   // is available.
   void EnableWildcardLoginCheck(const std::string& username);
diff --git a/chrome/browser/chromeos/power/process_data_collector.cc b/chrome/browser/chromeos/power/process_data_collector.cc
index d52c590..4f336e9 100644
--- a/chrome/browser/chromeos/power/process_data_collector.cc
+++ b/chrome/browser/chromeos/power/process_data_collector.cc
@@ -36,7 +36,7 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/system/procfs_util.h"
+#include "chrome/browser/ash/system/procfs_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 11ff8dfa..5449f16 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -26,6 +26,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -40,9 +43,6 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/sync/split_settings_sync_field_trial.h"
 #include "chrome/browser/chromeos/sync/turn_sync_on_helper.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
diff --git a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
index bdfeb30..35e3fa53 100644
--- a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/stl_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/fake_input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
-#include "chrome/browser/chromeos/system/fake_input_device_settings.h"
 #include "chrome/common/pref_names.h"
 #include "components/feedback/tracing_manager.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc b/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc
index 706f0dd..170962e 100644
--- a/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc
+++ b/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_notification.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/chromeos/web_applications/system_web_app_integration_test.h"
@@ -127,7 +128,7 @@
   proxy->Launch(
       *GetManager().GetAppIdForSystemApp(web_app::SystemAppType::HELP),
       ui::EventFlags::EF_NONE, apps::mojom::LaunchSource::kFromKeyboard,
-      display::kDefaultDisplayId);
+      apps::MakeWindowInfo(display::kDefaultDisplayId));
 
   navigation_observer.Wait();
   // The HELP app is 18, see DefaultAppName in
diff --git a/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
index a020fb5..114ee21 100644
--- a/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
+++ b/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc
@@ -36,6 +36,7 @@
          host == chrome::kChromeUIDeviceLogHost ||
          host == chrome::kChromeUIDomainReliabilityInternalsHost ||
          host == chrome::kChromeUIDownloadInternalsHost ||
+         host == chrome::kChromeUIFamilyLinkUserInternalsHost ||
          host == chrome::kChromeUIGCMInternalsHost ||
          host == chrome::kChromeUIInternalsHost ||
          host == chrome::kChromeUIInterstitialHost ||
@@ -56,7 +57,6 @@
          host == chrome::kChromeUISiteEngagementHost ||
          host == chrome::kChromeUISnippetsInternalsHost ||
          host == chrome::kChromeUISuggestionsHost ||
-         host == chrome::kChromeUISupervisedUserInternalsHost ||
          host == chrome::kChromeUISyncInternalsHost ||
          host == chrome::kChromeUITranslateInternalsHost ||
          host == chrome::kChromeUIUsbInternalsHost ||
diff --git a/chrome/browser/dev_ui_browser_resources.grd b/chrome/browser/dev_ui_browser_resources.grd
index e602404..c92f620 100644
--- a/chrome/browser/dev_ui_browser_resources.grd
+++ b/chrome/browser/dev_ui_browser_resources.grd
@@ -82,9 +82,9 @@
       </if>
 
       <if expr="enable_supervised_users">
-        <include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals\supervised_user_internals.html" allowexternalscript="true" type="BINDATA" />
-        <include name="IDR_SUPERVISED_USER_INTERNALS_CSS" file="resources\supervised_user_internals\supervised_user_internals.css" type="BINDATA" />
-        <include name="IDR_SUPERVISED_USER_INTERNALS_JS" file="resources\supervised_user_internals\supervised_user_internals.js" type="BINDATA" />
+        <include name="IDR_FAMILY_LINK_USER_INTERNALS_HTML" file="resources\family_link_user_internals\family_link_user_internals.html" allowexternalscript="true" type="BINDATA" />
+        <include name="IDR_FAMILY_LINK_USER_INTERNALS_CSS" file="resources\family_link_user_internals\family_link_user_internals.css" type="BINDATA" />
+        <include name="IDR_FAMILY_LINK_USER_INTERNALS_JS" file="resources\family_link_user_internals\family_link_user_internals.js" type="BINDATA" />
       </if>
       <include name="IDR_TRANSLATE_INTERNALS_HTML" file="../../components/translate/translate_internals/translate_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_TRANSLATE_INTERNALS_JS" file="../../components/translate/translate_internals/translate_internals.js" type="BINDATA" />
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
index 9d344fa..8123f45 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
@@ -56,6 +56,22 @@
   "block_until_verdict": 1
 })";
 
+constexpr char kBlockingScansForDlpAndMalwareWithCustomMessage[] = R"(
+{
+  "service_provider": "google",
+  "enable": [
+    {
+      "url_list": ["*"],
+      "tags": ["dlp", "malware"]
+    }
+  ],
+  "block_until_verdict": 1,
+  "custom_messages": [{
+    "message": "Custom message",
+    "learn_more_url": "http://www.example.com/"
+  }]
+})";
+
 base::string16 text() {
   return base::UTF8ToUTF16(std::string(100, 'a'));
 }
@@ -364,13 +380,8 @@
     // The dialog shows the failure or success message for the appropriate
     // access point and scan type.
     base::string16 final_message = dialog->GetMessageForTesting()->GetText();
-    int files_count = file_scan() ? 1 : 0;
-    base::string16 expected_message =
-        success()
-            ? l10n_util::GetPluralStringFUTF16(
-                  IDS_DEEP_SCANNING_DIALOG_SUCCESS_MESSAGE, files_count)
-            : l10n_util::GetPluralStringFUTF16(
-                  IDS_DEEP_SCANNING_DIALOG_UPLOAD_FAILURE_MESSAGE, files_count);
+    base::string16 expected_message = GetExpectedMessage();
+
     ASSERT_EQ(final_message, expected_message);
 
     // The top image is the failure/success one corresponding to the access
@@ -404,6 +415,16 @@
     ASSERT_FALSE(dialog->GetSideIconSpinnerForTesting());
   }
 
+  virtual base::string16 GetExpectedMessage() {
+    int files_count = file_scan() ? 1 : 0;
+    return success()
+               ? l10n_util::GetPluralStringFUTF16(
+                     IDS_DEEP_SCANNING_DIALOG_SUCCESS_MESSAGE, files_count)
+               : l10n_util::GetPluralStringFUTF16(
+                     IDS_DEEP_SCANNING_DIALOG_UPLOAD_FAILURE_MESSAGE,
+                     files_count);
+  }
+
   void DestructorCalled(ContentAnalysisDialog* dialog) override {
     // End the test once the dialog gets destroyed.
     CallQuitClosure();
@@ -418,6 +439,23 @@
   }
 };
 
+// Tests the behavior of the dialog in the same way as
+// ContentAnalysisDialogAppearanceBrowserTest but with a custom message set by
+// the admin.
+class ContentAnalysisDialogCustomMessageAppearanceBrowserTest
+    : public ContentAnalysisDialogAppearanceBrowserTest {
+ private:
+  base::string16 GetExpectedMessage() override {
+    int files_count = file_scan() ? 1 : 0;
+    return success()
+               ? l10n_util::GetPluralStringFUTF16(
+                     IDS_DEEP_SCANNING_DIALOG_SUCCESS_MESSAGE, files_count)
+               : l10n_util::GetStringFUTF16(
+                     IDS_DEEP_SCANNING_DIALOG_CUSTOM_MESSAGE,
+                     base::ASCIIToUTF16("Custom message"));
+  }
+};
+
 constexpr char kTestUrl[] = "https://google.com";
 
 }  // namespace
@@ -644,4 +682,63 @@
                         safe_browsing::DeepScanAccessPoint::DRAG_AND_DROP,
                         safe_browsing::DeepScanAccessPoint::PASTE)));
 
+IN_PROC_BROWSER_TEST_P(ContentAnalysisDialogCustomMessageAppearanceBrowserTest,
+                       Test) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  // Setup policies to enable deep scanning, its UI and the responses to be
+  // simulated.
+  safe_browsing::SetAnalysisConnector(
+      browser()->profile()->GetPrefs(), FILE_ATTACHED,
+      kBlockingScansForDlpAndMalwareWithCustomMessage);
+
+  SetStatusCallbackResponse(
+      safe_browsing::SimpleContentAnalysisResponseForTesting(success(),
+                                                             success()));
+
+  // Set up delegate test values.
+  FakeContentAnalysisDelegate::SetResponseDelay(kSmallDelay);
+  SetUpDelegate();
+
+  bool called = false;
+  base::RunLoop run_loop;
+  SetQuitClosure(run_loop.QuitClosure());
+
+  ContentAnalysisDelegate::Data data;
+
+  // Use a file path or text to validate the appearance of the dialog for both
+  // types of scans.
+  if (file_scan())
+    CreateFilesForTest({"foo.doc"}, {"content"}, &data);
+  else
+    data.text.emplace_back(text());
+  ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
+      browser()->profile(), GURL(kTestUrl), &data,
+      enterprise_connectors::AnalysisConnector::FILE_ATTACHED));
+
+  ContentAnalysisDelegate::CreateForWebContents(
+      browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
+      base::BindLambdaForTesting(
+          [this, &called](const ContentAnalysisDelegate::Data& data,
+                          const ContentAnalysisDelegate::Result& result) {
+            for (bool result : result.paths_results)
+              ASSERT_EQ(result, success());
+            called = true;
+          }),
+      access_point());
+  run_loop.Run();
+  EXPECT_TRUE(called);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    ContentAnalysisDialogCustomMessageAppearanceBrowserTest,
+    testing::Combine(
+        /*file_scan=*/testing::Bool(),
+        /*success=*/testing::Bool(),
+        /*access_point=*/
+        testing::Values(safe_browsing::DeepScanAccessPoint::UPLOAD,
+                        safe_browsing::DeepScanAccessPoint::DRAG_AND_DROP,
+                        safe_browsing::DeepScanAccessPoint::PASTE)));
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/connectors_service.cc b/chrome/browser/enterprise/connectors/connectors_service.cc
index f324ceb..30d33e4 100644
--- a/chrome/browser/enterprise/connectors/connectors_service.cc
+++ b/chrome/browser/enterprise/connectors/connectors_service.cc
@@ -303,20 +303,11 @@
   if (!policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid())
     return true;
 
-  policy::UserCloudPolicyManager* profile_policy_manager =
-      Profile::FromBrowserContext(context_)->GetUserCloudPolicyManager();
-  policy::MachineLevelUserCloudPolicyManager* browser_policy_manager =
-      g_browser_process->browser_policy_connector()
-          ->machine_level_user_cloud_policy_manager();
-
-  if (!profile_policy_manager || !browser_policy_manager ||
-      !profile_policy_manager->IsClientRegistered() ||
-      !browser_policy_manager->IsClientRegistered()) {
-    return false;
-  }
-
-  auto* profile_policy = profile_policy_manager->core()->store()->policy();
-  auto* browser_policy = browser_policy_manager->core()->store()->policy();
+  const enterprise_management::PolicyData* profile_policy =
+      chrome::enterprise_util::GetProfilePolicyData(
+          Profile::FromBrowserContext(context_));
+  const enterprise_management::PolicyData* browser_policy =
+      chrome::enterprise_util::GetBrowserPolicyData();
 
   if (!profile_policy || !browser_policy)
     return false;
diff --git a/chrome/browser/enterprise/util/affiliation.cc b/chrome/browser/enterprise/util/affiliation.cc
index 506d164a..87aec75 100644
--- a/chrome/browser/enterprise/util/affiliation.cc
+++ b/chrome/browser/enterprise/util/affiliation.cc
@@ -3,11 +3,53 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/enterprise/util/affiliation.h"
+
 #include <set>
 
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+
 namespace chrome {
 namespace enterprise_util {
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
+
+namespace {
+
+const enterprise_management::PolicyData* GetPolicyData(
+    policy::CloudPolicyManager* policy_manager) {
+  if (!policy_manager || !policy_manager->IsClientRegistered() ||
+      !policy_manager->core() || !policy_manager->core()->store()) {
+    return nullptr;
+  }
+
+  return policy_manager->core()->store()->policy();
+}
+
+}  // namespace
+
+const enterprise_management::PolicyData* GetProfilePolicyData(
+    Profile* profile) {
+  DCHECK(profile);
+  return GetPolicyData(profile->GetUserCloudPolicyManager());
+}
+
+const enterprise_management::PolicyData* GetBrowserPolicyData() {
+  if (!g_browser_process->browser_policy_connector())
+    return nullptr;
+
+  policy::MachineLevelUserCloudPolicyManager* policy_manager =
+      g_browser_process->browser_policy_connector()
+          ->machine_level_user_cloud_policy_manager();
+
+  return GetPolicyData(policy_manager);
+}
+
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
+
 bool IsProfileAffiliated(
     const enterprise_management::PolicyData& profile_policy,
     const enterprise_management::PolicyData& browser_policy) {
diff --git a/chrome/browser/enterprise/util/affiliation.h b/chrome/browser/enterprise/util/affiliation.h
index 55de0787..fb3e866 100644
--- a/chrome/browser/enterprise/util/affiliation.h
+++ b/chrome/browser/enterprise/util/affiliation.h
@@ -5,11 +5,27 @@
 #ifndef CHROME_BROWSER_ENTERPRISE_UTIL_AFFILIATION_H_
 #define CHROME_BROWSER_ENTERPRISE_UTIL_AFFILIATION_H_
 
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "device_management_backend.pb.h"
 
+class Profile;
+
 namespace chrome {
 namespace enterprise_util {
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
+
+// Returns the PolicyData corresponding to |profile|, or nullptr if it can't be
+// obtained.
+const enterprise_management::PolicyData* GetProfilePolicyData(Profile* profile);
+
+// Returns the PolicyData corresponding to the browser, or nullptr if it can't
+// be obtained.
+const enterprise_management::PolicyData* GetBrowserPolicyData();
+
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
+
 // Returns true if the profile and browser are managed by the same customer
 // (affiliated). This is determined by comparing affiliation IDs obtained in the
 // policy fetching response. If either policies has no affiliation IDs, this
diff --git a/chrome/browser/error_reporting/BUILD.gn b/chrome/browser/error_reporting/BUILD.gn
index 2faabc2..f9ba071 100644
--- a/chrome/browser/error_reporting/BUILD.gn
+++ b/chrome/browser/error_reporting/BUILD.gn
@@ -1,10 +1,19 @@
 # Copyright 2020 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/chromeos/ui_mode.gni")
 
 # TODO(crbug.com/1129544) This is currently disabled due to Windows DLL
 # thunking issues. Fix & re-enable.
-assert(is_linux || is_chromeos)
+assert(is_linux || is_chromeos_ash || is_chromeos_lacros)
+
+source_set("constants") {
+  sources = [
+    "constants.cc",
+    "constants.h",
+  ]
+  deps = [ "//build:chromeos_buildflags" ]
+}
 
 static_library("error_reporting") {
   sources = [
@@ -25,6 +34,12 @@
     "//services/network:network_service",
     "//services/network/public/cpp",
   ]
+  if (is_chromeos_ash || is_chromeos_lacros) {
+    sources += [ "chrome_js_error_report_processor_chromeos.cc" ]
+    deps += [ ":constants" ]
+  } else {
+    sources += [ "chrome_js_error_report_processor_nonchromeos.cc" ]
+  }
 }
 
 source_set("test_support") {
@@ -42,6 +57,9 @@
     "//components/crash/content/browser/error_reporting",
     "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
   ]
+  if (is_chromeos_ash || is_chromeos_lacros) {
+    data_deps = [ ":mock_chromeos_crash_reporter" ]
+  }
 }
 
 source_set("unit_test") {
@@ -61,3 +79,17 @@
     "//testing/gtest",
   ]
 }
+
+if (is_chromeos_ash || is_chromeos_lacros) {
+  executable("mock_chromeos_crash_reporter") {
+    testonly = true
+    sources = [ "mock_chromeos_crash_reporter.cc" ]
+
+    deps = [
+      ":constants",
+      "//base",
+      "//net",
+      "//third_party/crashpad/crashpad/third_party/cpp-httplib",
+    ]
+  }
+}
diff --git a/chrome/browser/error_reporting/chrome_js_error_report_processor.cc b/chrome/browser/error_reporting/chrome_js_error_report_processor.cc
index 9ec9a63..ac1414f 100644
--- a/chrome/browser/error_reporting/chrome_js_error_report_processor.cc
+++ b/chrome/browser/error_reporting/chrome_js_error_report_processor.cc
@@ -6,43 +6,32 @@
 
 #include <tuple>
 #include <utility>
-#include <vector>
 
 #include "base/callback.h"
 #include "base/callback_helpers.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
-#include "chrome/common/chrome_paths.h"
 #include "components/crash/content/browser/error_reporting/javascript_error_report.h"
 #include "components/crash/core/app/client_upload_info.h"
 #include "components/crash/core/app/crashpad.h"
 #include "components/feedback/redaction_tool.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
-#include "components/upload_list/crash_upload_list.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "net/base/escape.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
 
 namespace {
 
-constexpr char kCrashEndpointUrl[] = "https://clients2.google.com/cr/report";
-constexpr char kCrashEndpointStagingUrl[] =
-    "https://clients2.google.com/cr/staging_report";
 constexpr char kNoBrowserNoWindow[] = "NO_BROWSER";
 constexpr char kRegularTabbedWindow[] = "REGULAR_TABBED";
 constexpr char kWebAppWindow[] = "WEB_APP";
@@ -74,18 +63,6 @@
       .Redact(message);
 }
 
-using ParameterMap = std::map<std::string, std::string>;
-
-std::string BuildPostRequestQueryString(const ParameterMap& params) {
-  std::vector<std::string> query_parts;
-  for (const auto& kv : params) {
-    query_parts.push_back(base::StrCat(
-        {kv.first, "=",
-         net::EscapeQueryParamValue(kv.second, /*use_plus=*/false)}));
-  }
-  return base::JoinString(query_parts, "&");
-}
-
 std::string MapWindowTypeToString(WindowType window_type) {
   switch (window_type) {
     case WindowType::kRegularTabbed:
@@ -105,69 +82,19 @@
     : clock_(base::DefaultClock::GetInstance()) {}
 ChromeJsErrorReportProcessor::~ChromeJsErrorReportProcessor() = default;
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
-void ChromeJsErrorReportProcessor::UpdateReportDatabase(
-    std::string remote_report_id,
-    base::Time report_time) {
-  // Uploads.log format is "seconds_since_epoch,crash_id\n"
-  base::FilePath crash_dir_path;
-  if (!base::PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path)) {
-    VLOG(1) << "Nowhere to write uploads.log";
-    return;
-  }
-  base::FilePath upload_log_path =
-      crash_dir_path.AppendASCII(CrashUploadList::kReporterLogFilename);
-  base::File upload_log(upload_log_path,
-                        base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
-  if (!upload_log.IsValid()) {
-    VLOG(1) << "Could not open upload.log: "
-            << base::File::ErrorToString(upload_log.error_details());
-    return;
-  }
-  std::string line = base::StrCat({base::NumberToString(report_time.ToTimeT()),
-                                   ",", remote_report_id, "\n"});
-  // WriteAtCurrentPos because O_APPEND.
-  if (upload_log.WriteAtCurrentPos(line.c_str(), line.length()) !=
-      static_cast<int>(line.length())) {
-    VLOG(1) << "Could not write to upload.log";
-    return;
-  }
-}
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
-
-void ChromeJsErrorReportProcessor::OnRequestComplete(
-    std::unique_ptr<network::SimpleURLLoader> url_loader,
-    base::ScopedClosureRunner callback_runner,
-    base::Time report_time,
-    std::unique_ptr<std::string> response_body) {
-  if (response_body) {
-    VLOG(1) << "Uploaded crash report. ID: " << *response_body;
-    // On Chrome OS, we use a different format than other platforms. Since we
-    // will soon not call this function at all on Chrome OS (crbug.com/986166),
-    // don't bother writing code to write to that format.
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
-    base::ThreadPool::PostTaskAndReply(
-        FROM_HERE, {base::MayBlock()},
-        base::BindOnce(&ChromeJsErrorReportProcessor::UpdateReportDatabase,
-                       this, *response_body, report_time),
-        callback_runner.Release());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
-  } else {
-    LOG(ERROR) << "Failed to upload crash report";
-  }
-  // callback_runner may implicitly run the callback when we reach this line if
-  // we didn't add a task to update the report database.
-}
-
 // Returns the redacted, fixed-up error report if the user consented to have it
 // sent. Returns base::nullopt if the user did not consent or we otherwise
 // should not send the report. All the MayBlock work should be done in here.
 base::Optional<JavaScriptErrorReport>
 ChromeJsErrorReportProcessor::CheckConsentAndRedact(
     JavaScriptErrorReport error_report) {
+  // Consent is handled at the OS level by crash_reporter so we don't need to
+  // check it here for Chrome OS.
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   if (!crash_reporter::GetClientCollectStatsConsent()) {
     return base::nullopt;
   }
+#endif
 
   // Remove error message from stack trace before redaction, since redaction
   // might change the error message enough that we don't find it.
@@ -186,7 +113,6 @@
   std::string product_name;
   std::string version;
   std::string channel;
-  std::string os_version;
 };
 
 ChromeJsErrorReportProcessor::PlatformInfo
@@ -199,79 +125,9 @@
   crash_reporter::GetClientProductNameAndVersion(&info.product_name,
                                                  &info.version, &info.channel);
 #endif
-  int32_t os_major_version = 0;
-  int32_t os_minor_version = 0;
-  int32_t os_bugfix_version = 0;
-  GetOsVersion(os_major_version, os_minor_version, os_bugfix_version);
-  info.os_version = base::StringPrintf("%d.%d.%d", os_major_version,
-                                       os_minor_version, os_bugfix_version);
   return info;
 }
 
-void ChromeJsErrorReportProcessor::SendReport(
-    const GURL& url,
-    const std::string& body,
-    base::ScopedClosureRunner callback_runner,
-    base::Time report_time,
-    network::SharedURLLoaderFactory* loader_factory) {
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->method = "POST";
-  resource_request->url = url;
-
-  const auto traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("javascript_report_error", R"(
-      semantics {
-        sender: "JavaScript error reporter"
-        description:
-          "Chrome can send JavaScript errors that occur within built-in "
-          "component extensions and chrome:// webpages. If enabled, the error "
-          "message, along with information about Chrome and the operating "
-          "system, is sent to Google for debugging."
-        trigger:
-          "A JavaScript error occurs in a Chrome component extension (an "
-          "extension bundled with the Chrome browser, not downloaded "
-          "separately) or in certain chrome:// webpages."
-        data:
-          "The JavaScript error message, the version and channel of Chrome, "
-          "the URL of the extension or webpage, the line and column number of "
-          "the JavaScript code where the error occurred, and a stack trace of "
-          "the error."
-        destination: GOOGLE_OWNED_SERVICE
-      }
-      policy {
-        cookies_allowed: NO
-        setting:
-          "You can enable or disable this feature via 'Automatically send "
-          "usage statistics and crash reports to Google' in Chromium's "
-          "settings under Advanced, Privacy. (This is in System Settings on "
-          "Chromebooks.) This feature is enabled by default."
-        chrome_policy {
-          MetricsReportingEnabled {
-            policy_options {mode: MANDATORY}
-            MetricsReportingEnabled: false
-          }
-        }
-      })");
-
-  VLOG(1) << "Sending crash report: " << resource_request->url;
-
-  auto url_loader = network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-
-  if (!body.empty()) {
-    url_loader->AttachStringForUpload(body, "text/plain");
-  }
-
-  constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024;
-  network::SimpleURLLoader* loader = url_loader.get();
-  loader->DownloadToString(
-      loader_factory,
-      base::BindOnce(&ChromeJsErrorReportProcessor::OnRequestComplete, this,
-                     std::move(url_loader), std::move(callback_runner),
-                     report_time),
-      kCrashEndpointResponseMaxSizeInBytes);
-}
-
 // Finishes sending process once the MayBlock processing is done. On UI thread.
 void ChromeJsErrorReportProcessor::OnConsentCheckCompleted(
     base::ScopedClosureRunner callback_runner,
@@ -285,13 +141,7 @@
     return;
   }
 
-  std::string crash_endpoint_string = error_report->send_to_production_servers
-                                          ? GetCrashEndpoint()
-                                          : GetCrashEndpointStaging();
-
-  // TODO(https://crbug.com/986166): Use crash_reporter for Chrome OS.
   const auto platform = GetPlatformInfo();
-
   const GURL source(error_report->url);
   const auto product = error_report->product.empty() ? platform.product_name
                                                      : error_report->product;
@@ -311,8 +161,8 @@
   params["os"] = "ChromeOS";
 #else
   params["os"] = base::SysInfo::OperatingSystemName();
+  params["os_version"] = GetOsVersion();
 #endif
-  params["os_version"] = platform.os_version;
   constexpr char kSourceSystemParamName[] = "source_system";
   switch (error_report->source_system) {
     case JavaScriptErrorReport::SourceSystem::kUnknown:
@@ -345,15 +195,10 @@
   }
   if (error_report->app_locale)
     params["app_locale"] = std::move(*error_report->app_locale);
-  const GURL url(base::StrCat(
-      {crash_endpoint_string, "?", BuildPostRequestQueryString(params)}));
-  std::string body;
-  if (error_report->stack_trace) {
-    body = std::move(*error_report->stack_trace);
-  }
 
-  SendReport(url, body, std::move(callback_runner), report_time,
-             loader_factory.get());
+  SendReport(std::move(params), std::move(error_report->stack_trace),
+             error_report->send_to_production_servers,
+             std::move(callback_runner), report_time, loader_factory);
 }
 
 void ChromeJsErrorReportProcessor::CheckAndUpdateRecentErrorReports(
@@ -458,11 +303,14 @@
 
   base::ScopedClosureRunner callback_runner(std::move(completion_callback));
 
+  scoped_refptr<network::SharedURLLoaderFactory> loader_factory;
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   // loader_factory must be created on UI thread. Get it now while we still
   // know the browser_context pointer is valid.
-  scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
+  loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(browser_context)
           ->GetURLLoaderFactoryForBrowserProcess();
+#endif
 
   // Get browser uptime before swapping threads to reduce lag time between the
   // error report occurring and sending it off.
@@ -481,18 +329,3 @@
                      std::move(loader_factory), browser_process_uptime,
                      clock_->Now()));
 }
-
-std::string ChromeJsErrorReportProcessor::GetCrashEndpoint() {
-  return kCrashEndpointUrl;
-}
-
-std::string ChromeJsErrorReportProcessor::GetCrashEndpointStaging() {
-  return kCrashEndpointStagingUrl;
-}
-
-void ChromeJsErrorReportProcessor::GetOsVersion(int32_t& os_major_version,
-                                                int32_t& os_minor_version,
-                                                int32_t& os_bugfix_version) {
-  base::SysInfo::OperatingSystemVersionNumbers(
-      &os_major_version, &os_minor_version, &os_bugfix_version);
-}
diff --git a/chrome/browser/error_reporting/chrome_js_error_report_processor.h b/chrome/browser/error_reporting/chrome_js_error_report_processor.h
index 1ac534e..c702ad4a 100644
--- a/chrome/browser/error_reporting/chrome_js_error_report_processor.h
+++ b/chrome/browser/error_reporting/chrome_js_error_report_processor.h
@@ -9,10 +9,12 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/callback_helpers.h"
 #include "base/containers/flat_map.h"
+#include "base/optional.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
@@ -49,11 +51,30 @@
     return recent_error_reports_;
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Force the error report processor to use the less-commonly-used temp file
+  // solution for communicating with crash_reporter. This is normally only used
+  // on old kernels without memfd_create, so we don't get good unit test
+  // coverage unless we force it.
+  void set_force_non_memfd_for_test() { force_non_memfd_for_test_ = true; }
+#endif
+
  protected:
   // Non-tests should call ChromeJsErrorReportProcessor::Create() instead.
   ChromeJsErrorReportProcessor();
   ~ChromeJsErrorReportProcessor() override;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Returns the first element(s) of the crash_reporter argv. By default, this
+  // is just the command name (so {"/sbin/crash_reporter"}). Virtual so that
+  // tests can override and can provide additional arguments to the test binary
+  // if needed.
+  virtual std::vector<std::string> GetCrashReporterArgvStart();
+#else
+  // Determines the version of the OS we are on. Virtual so that tests can
+  // override. On Chrome OS, this information is added by the crash_reporter.
+  virtual std::string GetOsVersion();
+
   // Testing hook -- returns the URL we will send the error reports to. By
   // default, returns the real endpoint.
   virtual std::string GetCrashEndpoint();
@@ -63,13 +84,6 @@
   // default, returns the real staging endpoint.
   virtual std::string GetCrashEndpointStaging();
 
-  // Determines the version of the OS we are on. Virtual so that tests can
-  // override.
-  virtual void GetOsVersion(int32_t& os_major_version,
-                            int32_t& os_minor_version,
-                            int32_t& os_bugfix_version);
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   // Update the uploads.log file with a record of this error report. This
   // ensures that the error appears on chrome://crashes and is listed in the
   // feedback reports.
@@ -79,11 +93,7 @@
 
  private:
   struct PlatformInfo;
-
-  void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
-                         base::ScopedClosureRunner callback_runner,
-                         base::Time report_time,
-                         std::unique_ptr<std::string> response_body);
+  using ParameterMap = std::map<std::string, std::string>;
 
   base::Optional<JavaScriptErrorReport> CheckConsentAndRedact(
       JavaScriptErrorReport error_report);
@@ -114,6 +124,41 @@
       const JavaScriptErrorReport& error_report,
       bool* should_send);
 
+  void SendReport(
+      ParameterMap params,
+      base::Optional<std::string> stack_trace,
+      bool send_to_production_servers,
+      base::ScopedClosureRunner callback_runner,
+      base::Time report_time,
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Write the parameters (and the stack_trace, if present) into a string
+  // suitable for passing the crash_reporter. Returns the string.
+  //
+  // Format is the same key:length:value format used by Crashpad and Breakpad
+  // when talking to crash_reporter. Example:
+  // value1:5:abcdevalue2:10:hellothere
+  static std::string ParamsToCrashReporterString(
+      const ParameterMap& params,
+      const base::Optional<std::string>& stack_trace);
+
+  void SendReportViaCrashReporter(ParameterMap params,
+                                  base::Optional<std::string> stack_trace);
+
+  bool force_non_memfd_for_test_ = false;
+#else
+  // Turn the parameter key/value pairs into a list of parameters suitable for
+  // being the query part of a URL. Does URL escaping and such.
+  static std::string BuildPostRequestQueryString(const ParameterMap& params);
+
+  void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
+                         base::ScopedClosureRunner callback_runner,
+                         base::Time report_time,
+                         std::unique_ptr<std::string> response_body);
+
+#endif
+
   // For JavaScript error reports, a mapping of message+product+line+column to
   // the last time we sent an error message for that
   // message+product+line+column.
diff --git a/chrome/browser/error_reporting/chrome_js_error_report_processor_chromeos.cc b/chrome/browser/error_reporting/chrome_js_error_report_processor_chromeos.cc
new file mode 100644
index 0000000..f28b23f
--- /dev/null
+++ b/chrome/browser/error_reporting/chrome_js_error_report_processor_chromeos.cc
@@ -0,0 +1,187 @@
+// Copyright 2021 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/error_reporting/chrome_js_error_report_processor.h"
+
+#include <errno.h>
+
+#include <algorithm>
+
+#include "base/callback_helpers.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/error_reporting/constants.h"
+
+// Per the memfd_create man page, we need _GNU_SOURCE
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/mman.h>
+
+namespace {
+
+// The format used to communicate with crash_reporter keys treats ':' as special
+// (introducing the length of the value). Remove any :'s from the key names in
+// place.
+void ReplaceColonsWithUnderscores(std::string& key) {
+  std::replace(key.begin(), key.end(), ':', '_');
+}
+
+// Gets a File pointing to some temporary location. In some cases, we have to
+// do extra cleanup; pass an *unbound* ScopedClosureRunner in |cleanup| so that
+// this function can add the necessary cleanup function.
+// If |force_non_memfd_for_test| is true, we act as if the memfd call failed and
+// go to the temp file case. Since most machines have memfd_create implemented,
+// this is the only way to get some unit-test coverage on the non-memfd_create
+// path.
+base::File GetMemfdOrTempFile(base::ScopedClosureRunner& cleanup,
+                              bool force_non_memfd_for_test) {
+  DCHECK(!cleanup) << "cleanup must be unbound";
+  if (!force_non_memfd_for_test) {
+    int memfd = HANDLE_EINTR(memfd_create("javascript_error", 0));
+    if (memfd != -1) {
+      return base::File(memfd);
+    }
+
+    if (errno != ENOSYS) {
+      PLOG(ERROR)
+          << "Could not create memfd file for JavaScript error reporting";
+      return base::File(base::File::FILE_ERROR_FAILED);
+    }
+  }
+
+  // Note that some VMs and boards with old kernels don't have memfd_create
+  // implemented yet. Work around by creating a temp file.
+  base::FilePath output_path;
+  base::ScopedFILE output_file = CreateAndOpenTemporaryStream(&output_path);
+  if (!output_file) {
+    PLOG(ERROR)
+        << "memfd_create not implemented and cannot create temporary stream";
+    return base::File(base::File::FILE_ERROR_FAILED);
+  }
+
+  DLOG(WARNING) << "JavaScript error reporting: Falling back to temp file "
+                << output_path.value();
+
+  // Need to actually delete the temp file once we're done.
+  cleanup.ReplaceClosure(
+      base::BindOnce(base::GetDeleteFileCallback(), std::move(output_path)));
+  return base::FILEToFile(output_file.release());
+}
+
+}  // namespace
+
+std::vector<std::string>
+ChromeJsErrorReportProcessor::GetCrashReporterArgvStart() {
+  return {"/sbin/crash_reporter"};
+}
+
+std::string ChromeJsErrorReportProcessor::ParamsToCrashReporterString(
+    const ParameterMap& params,
+    const base::Optional<std::string>& stack_trace) {
+  std::string result;
+  for (const auto& param : params) {
+    std::string key = param.first;
+    const std::string& value = param.second;
+    ReplaceColonsWithUnderscores(key);
+    std::string value_length_string = base::NumberToString(value.length());
+    base::StrAppend(&result, {key, ":", value_length_string, ":", value});
+  }
+  if (stack_trace) {
+    const std::string& payload = stack_trace.value();
+
+    std::string value_length_string = base::NumberToString(payload.length());
+    base::StrAppend(
+        &result, {kJavaScriptStackKey, ":", value_length_string, ":", payload});
+  }
+
+  return result;
+}
+
+void ChromeJsErrorReportProcessor::SendReportViaCrashReporter(
+    ParameterMap params,
+    base::Optional<std::string> stack_trace) {
+  base::ScopedClosureRunner cleanup;
+  base::File output(GetMemfdOrTempFile(cleanup, force_non_memfd_for_test_));
+  if (!output.IsValid()) {
+    return;  // Already logged error message in GetMemfdOrTempFile.
+  }
+
+  std::string string_to_write =
+      ParamsToCrashReporterString(params, stack_trace);
+  if (output.WriteAtCurrentPos(string_to_write.data(),
+                               string_to_write.length()) !=
+      static_cast<int>(string_to_write.length())) {
+    PLOG(ERROR) << "Failed to write to crash_reporter pipe";
+    return;
+  }
+
+  base::LaunchOptions crash_reporter_options;
+  crash_reporter_options.fds_to_remap.emplace_back(output.GetPlatformFile(),
+                                                   output.GetPlatformFile());
+
+  std::vector<std::string> argv(GetCrashReporterArgvStart());
+  argv.insert(argv.end(),
+              {base::StrCat({"--chrome_memfd=",
+                             base::NumberToString(output.GetPlatformFile())}),
+               base::StrCat({"--pid=", base::NumberToString(getpid())}),
+               base::StrCat({"--uid=", base::NumberToString(geteuid())}),
+               "--error_key=jserror"});
+
+  base::Process process = base::LaunchProcess(argv, crash_reporter_options);
+  if (!process.IsValid()) {
+    PLOG(ERROR) << "Failed to launch " << base::JoinString(argv, " ");
+    return;
+  }
+
+  {
+    base::ScopedAllowBaseSyncPrimitives allow_wait_for_exit;
+    // Wait for crash_reporter to finish. We need to wait for it to finish
+    // before we delete the temporary files that may have been created in
+    // GetMemfdOrTempFile().
+    int return_code = 0;
+    constexpr base::TimeDelta kMaximumWait = base::TimeDelta::FromMinutes(1);
+    if (process.WaitForExitWithTimeout(kMaximumWait, &return_code)) {
+      if (return_code != 0) {
+        LOG(WARNING) << "crash_reporter subprocess failed with return value "
+                     << return_code
+                     << (return_code == -1 ? " or maybe crashed" : "");
+      }
+    } else {
+      // Kill the stuck process to avoid zombies.
+      LOG(WARNING) << "crash_reporter failed to complete within "
+                   << kMaximumWait;
+      process.Terminate(0, false /*wait*/);
+    }
+  }
+}
+
+void ChromeJsErrorReportProcessor::SendReport(
+    ParameterMap params,
+    base::Optional<std::string> stack_trace,
+    bool send_to_production_servers,
+    base::ScopedClosureRunner callback_runner,
+    base::Time report_time,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory) {
+  // On Chrome OS, send the report through the OS crash reporting system to
+  // get more metadata and to keep all the consent logic in one place. We need
+  // to do file I/O, so over to a blockable thread for the send and then back to
+  // the UI thread for the finished callback.
+  base::ThreadPool::PostTaskAndReply(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&ChromeJsErrorReportProcessor::SendReportViaCrashReporter,
+                     this, std::move(params), std::move(stack_trace)),
+      callback_runner.Release());
+}
diff --git a/chrome/browser/error_reporting/chrome_js_error_report_processor_nonchromeos.cc b/chrome/browser/error_reporting/chrome_js_error_report_processor_nonchromeos.cc
new file mode 100644
index 0000000..7bfa4012
--- /dev/null
+++ b/chrome/browser/error_reporting/chrome_js_error_report_processor_nonchromeos.cc
@@ -0,0 +1,181 @@
+// Copyright 2021 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/error_reporting/chrome_js_error_report_processor.h"
+
+#include <utility>
+
+#include "base/callback_helpers.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/upload_list/crash_upload_list.h"
+#include "net/base/escape.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr char kCrashEndpointUrl[] = "https://clients2.google.com/cr/report";
+constexpr char kCrashEndpointStagingUrl[] =
+    "https://clients2.google.com/cr/staging_report";
+
+}  // namespace
+
+void ChromeJsErrorReportProcessor::OnRequestComplete(
+    std::unique_ptr<network::SimpleURLLoader> url_loader,
+    base::ScopedClosureRunner callback_runner,
+    base::Time report_time,
+    std::unique_ptr<std::string> response_body) {
+  if (response_body) {
+    DVLOG(1) << "Uploaded crash report. ID: " << *response_body;
+    base::ThreadPool::PostTaskAndReply(
+        FROM_HERE, {base::MayBlock()},
+        base::BindOnce(&ChromeJsErrorReportProcessor::UpdateReportDatabase,
+                       this, std::move(*response_body), report_time),
+        callback_runner.Release());
+  } else {
+    DLOG(ERROR) << "Failed to upload crash report";
+  }
+  // callback_runner may implicitly run the callback when we reach this line if
+  // we didn't add a task to update the report database.
+}
+
+std::string ChromeJsErrorReportProcessor::BuildPostRequestQueryString(
+    const ParameterMap& params) {
+  std::vector<std::string> query_parts;
+  for (const auto& kv : params) {
+    query_parts.push_back(base::StrCat(
+        {kv.first, "=",
+         net::EscapeQueryParamValue(kv.second, /*use_plus=*/false)}));
+  }
+  return base::JoinString(query_parts, "&");
+}
+
+void ChromeJsErrorReportProcessor::UpdateReportDatabase(
+    std::string remote_report_id,
+    base::Time report_time) {
+  // Uploads.log format is "seconds_since_epoch,crash_id\n"
+  base::FilePath crash_dir_path;
+  if (!base::PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path)) {
+    DVLOG(1) << "Nowhere to write uploads.log";
+    return;
+  }
+  base::FilePath upload_log_path =
+      crash_dir_path.AppendASCII(CrashUploadList::kReporterLogFilename);
+  base::File upload_log(upload_log_path,
+                        base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
+  if (!upload_log.IsValid()) {
+    DVLOG(1) << "Could not open upload.log: "
+             << base::File::ErrorToString(upload_log.error_details());
+    return;
+  }
+  std::string line = base::StrCat({base::NumberToString(report_time.ToTimeT()),
+                                   ",", remote_report_id, "\n"});
+  // WriteAtCurrentPos because O_APPEND.
+  if (upload_log.WriteAtCurrentPos(line.c_str(), line.length()) !=
+      static_cast<int>(line.length())) {
+    DVLOG(1) << "Could not write to upload.log";
+    return;
+  }
+}
+
+std::string ChromeJsErrorReportProcessor::GetCrashEndpoint() {
+  return kCrashEndpointUrl;
+}
+
+std::string ChromeJsErrorReportProcessor::GetCrashEndpointStaging() {
+  return kCrashEndpointStagingUrl;
+}
+
+// On non-Chrome OS platforms, send the report directly.
+void ChromeJsErrorReportProcessor::SendReport(
+    ParameterMap params,
+    base::Optional<std::string> stack_trace,
+    bool send_to_production_servers,
+    base::ScopedClosureRunner callback_runner,
+    base::Time report_time,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory) {
+  std::string crash_endpoint_string = send_to_production_servers
+                                          ? GetCrashEndpoint()
+                                          : GetCrashEndpointStaging();
+
+  const GURL url(base::StrCat(
+      {crash_endpoint_string, "?", BuildPostRequestQueryString(params)}));
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "POST";
+  resource_request->url = url;
+
+  const auto traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("javascript_report_error", R"(
+      semantics {
+        sender: "JavaScript error reporter"
+        description:
+          "Chrome can send JavaScript errors that occur within built-in "
+          "component extensions and chrome:// webpages. If enabled, the error "
+          "message, along with information about Chrome and the operating "
+          "system, is sent to Google for debugging."
+        trigger:
+          "A JavaScript error occurs in a Chrome component extension (an "
+          "extension bundled with the Chrome browser, not downloaded "
+          "separately) or in certain chrome:// webpages."
+        data:
+          "The JavaScript error message, the version and channel of Chrome, "
+          "the URL of the extension or webpage, the line and column number of "
+          "the JavaScript code where the error occurred, and a stack trace of "
+          "the error."
+        destination: GOOGLE_OWNED_SERVICE
+      }
+      policy {
+        cookies_allowed: NO
+        setting:
+          "You can enable or disable this feature via 'Automatically send "
+          "usage statistics and crash reports to Google' in Chromium's "
+          "settings under Advanced, Privacy. (This is in System Settings on "
+          "Chromebooks.) This feature is enabled by default."
+        chrome_policy {
+          MetricsReportingEnabled {
+            policy_options {mode: MANDATORY}
+            MetricsReportingEnabled: false
+          }
+        }
+      })");
+
+  DVLOG(1) << "Sending crash report: " << resource_request->url;
+
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+
+  if (stack_trace) {
+    url_loader->AttachStringForUpload(*stack_trace, "text/plain");
+  }
+
+  constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024;
+  network::SimpleURLLoader* loader = url_loader.get();
+  loader->DownloadToString(
+      loader_factory.get(),
+      base::BindOnce(&ChromeJsErrorReportProcessor::OnRequestComplete, this,
+                     std::move(url_loader), std::move(callback_runner),
+                     report_time),
+      kCrashEndpointResponseMaxSizeInBytes);
+}
+
+std::string ChromeJsErrorReportProcessor::GetOsVersion() {
+  int32_t os_major_version = 0;
+  int32_t os_minor_version = 0;
+  int32_t os_bugfix_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(
+      &os_major_version, &os_minor_version, &os_bugfix_version);
+  return base::StrCat({base::NumberToString(os_major_version), ".",
+                       base::NumberToString(os_minor_version), ".",
+                       base::NumberToString(os_bugfix_version)});
+}
diff --git a/chrome/browser/error_reporting/chrome_js_error_report_processor_unittest.cc b/chrome/browser/error_reporting/chrome_js_error_report_processor_unittest.cc
index d05996b..245b942d 100644
--- a/chrome/browser/error_reporting/chrome_js_error_report_processor_unittest.cc
+++ b/chrome/browser/error_reporting/chrome_js_error_report_processor_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/simple_test_clock.h"
@@ -70,6 +71,10 @@
     run_loop.Run();
   }
 
+  // Helper for TEST_F(ChromeJsErrorReportProcessorTest, AllFields) and
+  // TEST_F(ChromeJsErrorReportProcessorTest, WorksWithoutMemfdCreate).
+  void TestAllFields();
+
  protected:
   base::SimpleTestClock test_clock_;
   content::BrowserTaskEnvironment task_environment_;
@@ -122,10 +127,13 @@
   EXPECT_THAT(actual_report->query,
               HasSubstr("full_url=https%3A%2F%2Fwww.chromium.org%2FHome"));
   EXPECT_THAT(actual_report->query, HasSubstr("url=%2FHome"));
-  // This is from MockChromeJsErrorReportProcessor::GetOsVersion()
-  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
   EXPECT_THAT(actual_report->query, HasSubstr("browser=Chrome"));
   EXPECT_THAT(actual_report->query, Not(HasSubstr("source_system=")));
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+  // This is from MockChromeJsErrorReportProcessor::GetOsVersion()
+  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
+#endif
   // These are from MockCrashEndpoint::Client::GetProductNameAndVersion, which
   // is only defined for non-MAC POSIX systems. TODO(https://crbug.com/1121816):
   // Get this info for non-POSIX platforms.
@@ -138,7 +146,7 @@
   EXPECT_EQ(actual_report->content, "");
 }
 
-TEST_F(ChromeJsErrorReportProcessorTest, AllFields) {
+void ChromeJsErrorReportProcessorTest::TestAllFields() {
   auto report = MakeErrorReport("Hello World");
   report.url = "https://www.chromium.org/Home";
   report.product = "Unit test";
@@ -168,8 +176,6 @@
   EXPECT_THAT(actual_report->query,
               HasSubstr("full_url=https%3A%2F%2Fwww.chromium.org%2FHome"));
   EXPECT_THAT(actual_report->query, HasSubstr("url=%2FHome"));
-  // This is from MockChromeJsErrorReportProcessor::GetOsVersion()
-  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
   EXPECT_THAT(actual_report->query, HasSubstr("browser=Chrome"));
   // product is double-escaped. The first time, it transforms to Unit%20test,
   // then the % is turned into %25.
@@ -178,6 +184,11 @@
   EXPECT_THAT(actual_report->query, HasSubstr("line=83"));
   EXPECT_THAT(actual_report->query, HasSubstr("column=14"));
   EXPECT_THAT(actual_report->query, HasSubstr("source_system=webui_observer"));
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+  // This is from MockChromeJsErrorReportProcessor::GetOsVersion()
+  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
+#endif
   // These are from MockCrashEndpoint::Client::GetProductNameAndVersion, which
   // is only defined for non-MAC POSIX systems. TODO(https://crbug.com/1121816):
   // Get this info for non-POSIX platforms.
@@ -188,6 +199,13 @@
   EXPECT_EQ(actual_report->content, "bad_func(1, 2)\nonclick()\n");
 }
 
+TEST_F(ChromeJsErrorReportProcessorTest, AllFields) {
+  TestAllFields();
+}
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+// On Chrome OS, consent checks are handled in the crash_reporter, not in the
+// browser.
 TEST_F(ChromeJsErrorReportProcessorTest, NoConsent) {
   endpoint_->set_consented(false);
   auto report = MakeErrorReport("Hello World");
@@ -198,6 +216,7 @@
 
   EXPECT_FALSE(endpoint_->last_report());
 }
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 TEST_F(ChromeJsErrorReportProcessorTest, StackTraceWithErrorMessage) {
   auto report = MakeErrorReport("Hello World");
@@ -511,3 +530,10 @@
                      << UploadInfoVectorToString(uploads);
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+TEST_F(ChromeJsErrorReportProcessorTest, WorksWithoutMemfdCreate) {
+  processor_->set_force_non_memfd_for_test();
+  TestAllFields();
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/error_reporting/constants.cc b/chrome/browser/error_reporting/constants.cc
new file mode 100644
index 0000000..774c90b
--- /dev/null
+++ b/chrome/browser/error_reporting/constants.cc
@@ -0,0 +1,9 @@
+// Copyright 2021 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/error_reporting/constants.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+const char kJavaScriptStackKey[] = "upload_file_js_stack\"; filename=\"stack\"";
+#endif
diff --git a/chrome/browser/error_reporting/constants.h b/chrome/browser/error_reporting/constants.h
new file mode 100644
index 0000000..2d96c57
--- /dev/null
+++ b/chrome/browser/error_reporting/constants.h
@@ -0,0 +1,19 @@
+// Copyright 2021 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_ERROR_REPORTING_CONSTANTS_H_
+#define CHROME_BROWSER_ERROR_REPORTING_CONSTANTS_H_
+
+#include "build/chromeos_buildflags.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+// The key we pass to crash_reporter to indicate this key/value pair is the
+// JavaScript stack payload.
+// The format of the key needs to match Chrome OS's
+// ChromeCollector::ParseCrashLog and kDefaultJavaScriptStackName. The
+// 'filename' within the key doesn't actually matter but must be present.
+extern const char kJavaScriptStackKey[];
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+
+#endif  // CHROME_BROWSER_ERROR_REPORTING_CONSTANTS_H_
diff --git a/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.cc b/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.cc
index 5437218c..a6a40e45 100644
--- a/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.cc
+++ b/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.cc
@@ -4,8 +4,13 @@
 
 #include "chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h"
 
+#include "base/base_paths.h"
 #include "base/check.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "components/crash/content/browser/error_reporting/javascript_error_report.h"
 #include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
 
@@ -44,6 +49,24 @@
   crash_endpoint_staging_ = crash_endpoint;
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+std::vector<std::string>
+MockChromeJsErrorReportProcessor::GetCrashReporterArgvStart() {
+  // Redirect uploads to our a simple upload shim which will then send them to
+  // the MockCrashEndpoint. This simulates the Chrome OS crash_reporter and
+  // crash_sender in a way that allows most tests to run without changes.
+  base::FilePath mock_crash_reporter_path;
+  CHECK(base::PathService::Get(base::DIR_EXE, &mock_crash_reporter_path));
+  mock_crash_reporter_path =
+      mock_crash_reporter_path.Append("mock_chromeos_crash_reporter");
+  return {mock_crash_reporter_path.value(),
+          base::StrCat({"--upload_to=", crash_endpoint_})};
+}
+#else
+std::string MockChromeJsErrorReportProcessor::GetOsVersion() {
+  return "7.20.1";
+}
+
 std::string MockChromeJsErrorReportProcessor::GetCrashEndpoint() {
   return crash_endpoint_;
 }
@@ -52,16 +75,6 @@
   return crash_endpoint_staging_;
 }
 
-void MockChromeJsErrorReportProcessor::GetOsVersion(
-    int32_t& os_major_version,
-    int32_t& os_minor_version,
-    int32_t& os_bugfix_version) {
-  os_major_version = 7;
-  os_minor_version = 20;
-  os_bugfix_version = 1;
-}
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 void MockChromeJsErrorReportProcessor::UpdateReportDatabase(
     std::string remote_report_id,
     base::Time report_time) {
diff --git a/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h b/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h
index 015ca699..2b8dcec 100644
--- a/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h
+++ b/chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h
@@ -9,8 +9,10 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
 #include "base/test/scoped_path_override.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/error_reporting/chrome_js_error_report_processor.h"
@@ -53,15 +55,13 @@
 #endif
 
  protected:
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  std::vector<std::string> GetCrashReporterArgvStart() override;
+#else
+  // Always returns "7.20.1" (arbitrary).
+  std::string GetOsVersion() override;
   std::string GetCrashEndpoint() override;
   std::string GetCrashEndpointStaging() override;
-
-  // Always returns 7.20.1 (arbitrary).
-  void GetOsVersion(int32_t& os_major_version,
-                    int32_t& os_minor_version,
-                    int32_t& os_bugfix_version) override;
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   void UpdateReportDatabase(std::string remote_report_id,
                             base::Time report_time) override;
 #endif
diff --git a/chrome/browser/error_reporting/mock_chromeos_crash_reporter.cc b/chrome/browser/error_reporting/mock_chromeos_crash_reporter.cc
new file mode 100644
index 0000000..551e714
--- /dev/null
+++ b/chrome/browser/error_reporting/mock_chromeos_crash_reporter.cc
@@ -0,0 +1,177 @@
+// Copyright 2021 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.
+
+// A simple binary that replicates the crash_reporter / crash_sender
+// functionality of Chrome OS for testing purposes. In particular, it has a
+// stripped-down version of the parsing logic in
+// src/platform2/crash-reporter/chrome_collector.cc, coupled with a simple
+// upload function similar to src/platform2/crash-reporter/crash_sender_util.cc
+// (but without the compression). This is used in tests to substitute for the
+// actual OS crash reporting system.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/error_reporting/constants.h"
+#include "net/base/escape.h"
+#include "net/http/http_status_code.h"
+#include "third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Parses the key:length:value triplets similar to
+// ChromeCollector::ParseCrashLog. Input is the file descriptor |fd|,
+// return is a list of key/value pairs in |values| and a payload in |payload|.
+//
+// Closes |fd| when done.
+bool ParseTriplets(int fd,
+                   std::map<std::string, std::string>& values,
+                   std::string& payload) {
+  base::ScopedFILE stream(fdopen(fd, "rb"));
+  if (!stream.get()) {
+    PLOG(ERROR) << "fdopen failed!";
+    return false;
+  }
+  std::string data;
+  if (!base::ReadStreamToString(stream.get(), &data)) {
+    LOG(WARNING) << "Read failed!";
+  }
+
+  std::string::size_type pos = 0;
+  while (pos < data.size()) {
+    std::string::size_type end_of_key = data.find(':', pos);
+    if (end_of_key == std::string::npos) {
+      LOG(ERROR) << "Incomplete value found, starting at position " << pos;
+      return false;
+    }
+
+    std::string key = data.substr(pos, end_of_key - pos);
+    std::string::size_type end_of_length = data.find(':', end_of_key + 1);
+    if (end_of_length == std::string::npos) {
+      LOG(ERROR) << "Incomplete length found, starting at position "
+                 << (end_of_key + 1);
+      return false;
+    }
+
+    std::string length_string =
+        data.substr(end_of_key + 1, end_of_length - (end_of_key + 1));
+    size_t length;
+    if (!base::StringToSizeT(length_string, &length)) {
+      LOG(ERROR) << "Bad length string '" << length_string << "'";
+      return false;
+    }
+
+    std::string value = data.substr(end_of_length + 1, length);
+    pos = end_of_length + length + 1;
+
+    if (key == kJavaScriptStackKey) {
+      payload = std::move(value);
+    } else {
+      values.emplace(std::move(key), std::move(value));
+    }
+  }
+  return true;
+}
+
+// Upload the error report to the provided URL.
+bool UploadViaHttp(const std::string& base_url,
+                   const std::map<std::string, std::string>& values,
+                   const std::string& payload) {
+  std::vector<std::string> query_parts;
+  for (const auto& kv : values) {
+    query_parts.emplace_back(base::StrCat(
+        {net::EscapeQueryParamValue(kv.first, /*use_plus=*/false), "=",
+         net::EscapeQueryParamValue(kv.second, /*use_plus=*/false)}));
+  }
+  std::string upload_str =
+      base::StrCat({base_url, "?", base::JoinString(query_parts, "&")});
+
+  GURL upload_url(upload_str);
+  if (!upload_url.is_valid()) {
+    LOG(ERROR) << "Invalid upload_to URL: '" << upload_str << "'";
+    return false;
+  }
+
+  // Upload using httplib. The normal Chromium way (SimpleURLLoader) needs a lot
+  // of browser stuff to be set up before it can be used, so we use the
+  // standalone httplib in this test binary.
+  std::string host = upload_url.host();
+  httplib::Client cli(host.c_str(), upload_url.EffectiveIntPort());
+  if (!cli.is_valid()) {
+    LOG(ERROR) << "httplib::Client setup error";
+    return false;
+  }
+
+  std::string path = upload_url.PathForRequest();
+  auto response = cli.Post(path.c_str(), payload, "text/plain");
+  if (!response) {
+    LOG(ERROR) << "No response to Post";
+    return false;
+  }
+
+  if (response->status != net::HTTP_OK) {
+    LOG(ERROR) << "http response " << response->status;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+  constexpr char kFdSwitch[] = "chrome_memfd";
+  if (!cmd_line->HasSwitch(kFdSwitch)) {
+    LOG(ERROR) << "No --chrome_memfd";
+    return EXIT_FAILURE;
+  }
+  auto fd_string = cmd_line->GetSwitchValueASCII(kFdSwitch);
+  int fd;
+  if (!base::StringToInt(fd_string, &fd)) {
+    LOG(ERROR) << "Can't parse --chrome_memfd '" << fd_string << "' as int";
+    return EXIT_FAILURE;
+  }
+
+  // Note: This must be a map (not an unordered_map or such) because some unit
+  // tests rely on the order of the parameters in the URL string. Until that's
+  // fixed, keep the values sorted by key in the URL.
+  std::map<std::string, std::string> values;
+  std::string payload;
+  if (!ParseTriplets(fd, values, payload)) {
+    return EXIT_FAILURE;
+  }
+
+  constexpr char kUploadSwitch[] = "upload_to";
+  if (!cmd_line->HasSwitch(kUploadSwitch)) {
+    LOG(ERROR) << "No --upload_to";
+    return EXIT_FAILURE;
+  }
+  std::string base_url = cmd_line->GetSwitchValueASCII(kUploadSwitch);
+  if (!UploadViaHttp(base_url, values, payload)) {
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index fbec527..2b2336a 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -88,6 +88,9 @@
   void SetWebViewPartitionID(const std::string& partition_id) override {}
   void SetScriptingAllowlist(
       const std::vector<std::string>& extension_ids) override {}
+  void UpdateDefaultPolicyHostRestrictions(
+      const URLPatternSet& default_policy_blocked_hosts,
+      const URLPatternSet& default_policy_allowed_hosts) override {}
 
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
 };
diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc b/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
index 09c46fdc..b503e71 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
@@ -59,13 +59,10 @@
   profile->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks, list);
   ASSERT_EQ(2u, managed->managed_node()->children().size());
 
-  if (GetParam() == ContextType::kEventPage) {
-    ASSERT_TRUE(RunExtensionTest("bookmarks")) << message_;
-  } else {
-    ASSERT_TRUE(RunExtensionTestWithFlags(
-        "bookmarks", kFlagRunAsServiceWorkerBasedExtension, kFlagNone))
-        << message_;
-  }
+  ASSERT_TRUE(RunExtensionTest(
+      {.name = "bookmarks"},
+      {.load_as_service_worker = GetParam() == ContextType::kServiceWorker}))
+      << message_;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
index 43682fb..2d8f632 100644
--- a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
+++ b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
@@ -32,9 +32,8 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ServiceWorkerContextMenus) {
-  ASSERT_TRUE(RunExtensionTestWithFlags("context_menus/event_page",
-                                        kFlagRunAsServiceWorkerBasedExtension,
-                                        kFlagNone))
+  ASSERT_TRUE(RunExtensionTest({.name = "context_menus/event_page"},
+                               {.load_as_service_worker = true}))
       << message_;
 }
 
diff --git a/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc b/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
index 4484e8c..a3443a2 100644
--- a/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
+++ b/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
@@ -119,7 +119,7 @@
                    "\\d+&browser_"
                    "version=1.2.3.4&channel=Stable&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2F&"
-                   "os=ChromeOS&os_version=7.20.1"
+                   "os=ChromeOS"
                    "&prod=Chrome_ChromeOS&renderer_process_uptime_ms=\\d+&"
                    "source_system=crash_report_api&src="
                    "http%3A%2F%2Fwww.test."
@@ -152,7 +152,7 @@
                    "\\d+&browser_"
                    "version=1.2.3.4&channel=Stable&column=456&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2Ffoo"
-                   "&line=123&os=ChromeOS&os_version=7.20.1"
+                   "&line=123&os=ChromeOS"
                    "&prod=Chrome%2520\\(Chrome%2520OS\\)&renderer_process_"
                    "uptime_ms=\\d+&"
                    "source_system=crash_report_api&"
@@ -184,7 +184,7 @@
                    "\\d+&browser_version=1.2."
                    "3.4&channel=Stable&column=456&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2Ffoo&"
-                   "line=123&os=ChromeOS&os_version=7.20.1"
+                   "line=123&os=ChromeOS"
                    "&prod=TestApp&renderer_process_uptime_ms=\\d+&"
                    "source_system=crash_report_api&src=http%3A%"
                    "2F%2Fwww.test.com%2Ffoo&type="
@@ -218,8 +218,7 @@
           "3.4&channel=Stable&column=456&"
           "error_message=%5BMAC%20OUI%3D06%3A00%3A00%20IFACE%3D1%5D&"
           "full_url=http%3A%2F%2Fwww.test.com%2Ffoo&line=123&os=ChromeOS&"
-          "os_version=7.20.1"
-          "&prod=TestApp&renderer_process_uptime_ms=\\d+&"
+          "prod=TestApp&renderer_process_uptime_ms=\\d+&"
           "source_system=crash_report_api&src=http%3A%2F%2Fwww."
           "test.com%2Ffoo&type="
           "JavascriptError&url=%2Ffoo&ver=1.0.0.0"));
@@ -259,27 +258,6 @@
   ASSERT_FALSE(report);
 }
 
-// Ensures that reportError checks user consent for data collection on the
-// correct thread and correctly handles the case where consent is not given.
-IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, NoConsent) {
-  constexpr char kTestScript[] = R"(
-    chrome.crashReportPrivate.reportError({
-        message: "hi",
-        url: "http://www.test.com",
-      },
-      () => {
-        window.domAutomationController.send(chrome.runtime.lastError ?
-            chrome.runtime.lastError.message : "")
-      });
-  )";
-
-  crash_endpoint_->set_consented(false);
-  EXPECT_EQ("", ExecuteScriptInBackgroundPage(extension_->id(), kTestScript));
-  // The server should not receive any reports.
-  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
-  EXPECT_FALSE(report);
-}
-
 // Test REGULAR_TABBED is detected when |CrashReportPrivate| is called from a
 // tab's |web_contents|.
 IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, CalledFromWebContentsInTab) {
@@ -310,7 +288,7 @@
                    "\\d+&browser_"
                    "version=1.2.3.4&channel=Stable&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2F&"
-                   "os=ChromeOS&os_version=7.20.1"
+                   "os=ChromeOS"
                    "&prod=Chrome_ChromeOS&renderer_process_uptime_ms=\\d+&"
                    "source_system=crash_report_api&src="
                    "http%3A%2F%2Fwww.test."
@@ -361,7 +339,7 @@
                    "\\d+&browser_"
                    "version=1.2.3.4&channel=Stable&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2F&"
-                   "os=ChromeOS&os_version=7.20.1"
+                   "os=ChromeOS"
                    "&prod=Chrome_ChromeOS&renderer_process_uptime_ms=\\d+&"
                    "source_system=crash_report_api&src="
                    "http%3A%2F%2Fwww.test."
@@ -396,7 +374,7 @@
                    "\\d+&browser_"
                    "version=1.2.3.4&channel=Stable&"
                    "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2F&"
-                   "os=ChromeOS&os_version=7.20.1"
+                   "os=ChromeOS"
                    "&prod=Chrome_ChromeOS&renderer_process_uptime_ms=\\d+&"
                    "source_system=crash_report_api&src="
                    "http%3A%2F%2Fwww.test."
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index a6c583b1..443ddce5 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -20,10 +20,10 @@
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
-#include "extensions/browser/declarative_user_script_manager.h"
 #include "extensions/browser/extension_action.h"
 #include "extensions/browser/extension_action_manager.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/api/declarative/declarative_constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_messages.h"
@@ -321,7 +321,8 @@
   HostID host_id(HostID::EXTENSIONS, extension->id());
   InitScript(host_id, extension, script_data);
 
-  script_set_ = DeclarativeUserScriptManager::Get(browser_context)
+  script_set_ = ExtensionSystem::Get(browser_context)
+                    ->user_script_manager()
                     ->GetDeclarativeUserScriptSetByID(host_id);
   AddScript();
 }
diff --git a/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc b/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc
index a45f0f1a..22ea2b4 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc
@@ -20,6 +20,7 @@
 #include "extensions/browser/extension_action.h"
 #include "extensions/browser/extension_action_manager.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/api/declarative/declarative_constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -57,8 +58,10 @@
   // issue is fixed.
   virtual void Init() {
     InitializeEmptyExtensionService();
-    static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))->
-        SetReady();
+    auto* extension_system =
+        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
+    extension_system->CreateUserScriptManager();
+    extension_system->SetReady();
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
index 2b8067b8..4bec694 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
@@ -50,11 +50,9 @@
   }
 
   bool RunTest(const std::string& extension_path) {
-    if (GetParam() != ContextType::kServiceWorker) {
-      return RunExtensionTest(extension_path);
-    }
-    return RunExtensionTestWithFlags(
-        extension_path, kFlagRunAsServiceWorkerBasedExtension, kFlagNone);
+    return RunExtensionTest(
+        {.name = extension_path.c_str()},
+        {.load_as_service_worker = GetParam() == ContextType::kServiceWorker});
   }
 
  private:
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
index 2b75de2..1c12f6f 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
@@ -6,12 +6,20 @@
 
 #include <memory>
 
+#include "base/callback_forward.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/enterprise/util/affiliation.h"
+#include "chrome/browser/profiles/profile.h"
+#include "device_management_backend.pb.h"
+
 namespace extensions {
 namespace enterprise_reporting {
 
 ContextInfoFetcher::ContextInfoFetcher(
+    content::BrowserContext* browser_context,
     enterprise_connectors::ConnectorsService* connectors_service)
-    : connectors_service_(connectors_service) {
+    : browser_context_(browser_context),
+      connectors_service_(connectors_service) {
   DCHECK(connectors_service_);
 }
 
@@ -19,13 +27,15 @@
 
 // static
 std::unique_ptr<ContextInfoFetcher> ContextInfoFetcher::CreateInstance(
+    content::BrowserContext* browser_context,
     enterprise_connectors::ConnectorsService* connectors_service) {
   // TODO(domfc): Add platform overrides of the class once they are needed for
   // an attribute.
-  return std::make_unique<ContextInfoFetcher>(connectors_service);
+  return std::make_unique<ContextInfoFetcher>(browser_context,
+                                              connectors_service);
 }
 
-api::enterprise_reporting_private::ContextInfo ContextInfoFetcher::Fetch() {
+void ContextInfoFetcher::Fetch(ContextInfoCallback callback) {
   api::enterprise_reporting_private::ContextInfo info;
 
   info.browser_affiliation_ids = GetBrowserAffiliationIDs();
@@ -40,17 +50,37 @@
   info.on_security_event_providers = GetOnSecurityEventProviders();
   info.browser_version = GetBrowserVersion();
 
-  return info;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(info)));
 }
 
 std::vector<std::string> ContextInfoFetcher::GetBrowserAffiliationIDs() {
-  // TODO(crbug.com/1169200): Add code to get the affiliation IDs.
-  return {};
+  const enterprise_management::PolicyData* browser_policy_data =
+      chrome::enterprise_util::GetBrowserPolicyData();
+
+  if (!browser_policy_data ||
+      browser_policy_data->device_affiliation_ids().empty()) {
+    return {};
+  }
+
+  const auto& affiliation_ids = browser_policy_data->device_affiliation_ids();
+  return std::vector<std::string>(affiliation_ids.begin(),
+                                  affiliation_ids.end());
 }
 
 std::vector<std::string> ContextInfoFetcher::GetProfileAffiliationIDs() {
-  // TODO(crbug.com/1169212): Add code to get the affiliation IDs.
-  return {};
+  const enterprise_management::PolicyData* profile_policy_data =
+      chrome::enterprise_util::GetProfilePolicyData(
+          Profile::FromBrowserContext(browser_context_));
+
+  if (!profile_policy_data ||
+      profile_policy_data->user_affiliation_ids().empty()) {
+    return {};
+  }
+
+  const auto& affiliation_ids = profile_policy_data->user_affiliation_ids();
+  return std::vector<std::string>(affiliation_ids.begin(),
+                                  affiliation_ids.end());
 }
 
 std::vector<std::string> ContextInfoFetcher::GetAnalysisConnectorProviders(
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
index 6ac67d64..d68a8f8 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
@@ -5,9 +5,14 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_CONTEXT_INFO_FETCHER_H_
 #define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_CONTEXT_INFO_FETCHER_H_
 
+#include "base/callback_forward.h"
 #include "chrome/browser/enterprise/connectors/connectors_service.h"
 #include "chrome/common/extensions/api/enterprise_reporting_private.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
 namespace extensions {
 namespace enterprise_reporting {
 
@@ -16,7 +21,10 @@
 // has its own subclass implementation.
 class ContextInfoFetcher {
  public:
-  explicit ContextInfoFetcher(
+  using ContextInfoCallback =
+      base::OnceCallback<void(api::enterprise_reporting_private::ContextInfo)>;
+  ContextInfoFetcher(
+      content::BrowserContext* browser_context,
       enterprise_connectors::ConnectorsService* connectors_service);
   virtual ~ContextInfoFetcher();
 
@@ -25,10 +33,14 @@
 
   // Returns a platform specific instance of ContextInfoFetcher.
   static std::unique_ptr<ContextInfoFetcher> CreateInstance(
+      content::BrowserContext* browser_context,
       enterprise_connectors::ConnectorsService* connectors_service);
 
-  // Fetches the device information for the current platform.
-  api::enterprise_reporting_private::ContextInfo Fetch();
+  // Fetches the context information for the current platform. Eventually calls
+  // |callback_|. This function takes a callback to return a ContextInfo instead
+  // of returning synchronously because some attributes need to be fetched
+  // asynchronously.
+  void Fetch(ContextInfoCallback callback);
 
  private:
   // The following private methods each populate an attribute of ContextInfo. If
@@ -49,6 +61,8 @@
 
   std::string GetBrowserVersion();
 
+  content::BrowserContext* browser_context_;
+
   // |connectors_service| is used to obtain the value of each Connector policy.
   enterprise_connectors::ConnectorsService* connectors_service_;
 };
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index 361587e..3ee39eb 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -282,25 +282,13 @@
       enterprise_connectors::ConnectorsServiceFactory::GetInstance()
           ->GetForBrowserContext(browser_context());
   DCHECK(connectors_service);
-#if defined(OS_WIN)
-  base::PostTaskAndReplyWithResult(
-      base::ThreadPool::CreateCOMSTATaskRunner({}).get(), FROM_HERE,
-      base::BindOnce(&enterprise_reporting::ContextInfoFetcher::Fetch,
-                     enterprise_reporting::ContextInfoFetcher::CreateInstance(
-                         connectors_service)),
-      base::BindOnce(&EnterpriseReportingPrivateGetContextInfoFunction::
-                         OnContextInfoRetrieved,
-                     this));
-#else
-  base::PostTaskAndReplyWithResult(
-      base::ThreadPool::CreateTaskRunner({}).get(), FROM_HERE,
-      base::BindOnce(&enterprise_reporting::ContextInfoFetcher::Fetch,
-                     enterprise_reporting::ContextInfoFetcher::CreateInstance(
-                         connectors_service)),
-      base::BindOnce(&EnterpriseReportingPrivateGetContextInfoFunction::
-                         OnContextInfoRetrieved,
-                     this));
-#endif  // defined(OS_WIN)
+
+  context_info_fetcher_ =
+      enterprise_reporting::ContextInfoFetcher::CreateInstance(
+          browser_context(), connectors_service);
+  context_info_fetcher_->Fetch(base::BindOnce(
+      &EnterpriseReportingPrivateGetContextInfoFunction::OnContextInfoRetrieved,
+      this));
 
   return RespondLater();
 }
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
index 1486fba..85845486 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
@@ -10,6 +10,7 @@
 
 #include "chrome/browser/enterprise/signals/device_info_fetcher.h"
 #include "chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h"
+#include "chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h"
 #include "chrome/common/extensions/api/enterprise_reporting_private.h"
 #include "extensions/browser/extension_function.h"
 
@@ -157,6 +158,9 @@
   // Callback once the context data is retrieved.
   void OnContextInfoRetrieved(
       api::enterprise_reporting_private::ContextInfo context_info);
+
+  std::unique_ptr<enterprise_reporting::ContextInfoFetcher>
+      context_info_fetcher_;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc
new file mode 100644
index 0000000..27a9b7d
--- /dev/null
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc
@@ -0,0 +1,144 @@
+// Copyright 2021 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/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/extensions/api/enterprise_reporting_private.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
+#include "components/enterprise/browser/enterprise_switches.h"
+#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#include "content/public/test/browser_test.h"
+
+namespace enterprise_reporting_private =
+    ::extensions::api::enterprise_reporting_private;
+
+namespace extensions {
+
+namespace {
+
+constexpr char kBrowserID1[] = "browser_id_1";
+constexpr char kBrowserID2[] = "browser_id_2";
+constexpr char kProfileID1[] = "profile_id_1";
+constexpr char kProfileID2[] = "profile_id_2";
+
+}  // namespace
+
+// This browser test class is used to avoid mocking too much browser/profile
+// management objects in order to keep the test simple and useful. Please add
+// new tests for the getContextInfo API in enterprise_reporting_private_unittest
+// and only add tests in this file if they have similar constraints.
+class EnterpriseReportingPrivateGetContextInfoBrowserTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<testing::tuple<bool, bool>> {
+ public:
+  EnterpriseReportingPrivateGetContextInfoBrowserTest() {
+    if (browser_managed()) {
+      browser_dm_token_storage_ =
+          std::make_unique<policy::FakeBrowserDMTokenStorage>();
+      browser_dm_token_storage_->SetEnrollmentToken("enrollment_token");
+      browser_dm_token_storage_->SetClientId("id");
+      browser_dm_token_storage_->SetDMToken("dm_token");
+      policy::BrowserDMTokenStorage::SetForTesting(
+          browser_dm_token_storage_.get());
+    }
+  }
+
+  bool browser_managed() const { return testing::get<0>(GetParam()); }
+
+  bool profile_managed() const { return testing::get<1>(GetParam()); }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    if (browser_managed()) {
+      auto* browser_policy_manager =
+          g_browser_process->browser_policy_connector()
+              ->machine_level_user_cloud_policy_manager();
+      auto browser_policy_data =
+          std::make_unique<enterprise_management::PolicyData>();
+      browser_policy_data->add_device_affiliation_ids(kBrowserID1);
+      browser_policy_data->add_device_affiliation_ids(kBrowserID2);
+      browser_policy_manager->core()->store()->set_policy_data_for_testing(
+          std::move(browser_policy_data));
+    }
+
+    if (profile_managed()) {
+      safe_browsing::SetProfileDMToken(browser()->profile(), "dm_token");
+      auto* profile_policy_manager =
+          browser()->profile()->GetUserCloudPolicyManager();
+      auto profile_policy_data =
+          std::make_unique<enterprise_management::PolicyData>();
+      profile_policy_data->add_user_affiliation_ids(kProfileID1);
+      profile_policy_data->add_user_affiliation_ids(kProfileID2);
+      profile_policy_manager->core()->store()->set_policy_data_for_testing(
+          std::move(profile_policy_data));
+    }
+  }
+
+#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) && !BUILDFLAG(IS_CHROMEOS_ASH)
+  void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
+    InProcessBrowserTest::SetUpDefaultCommandLine(command_line);
+    command_line->AppendSwitch(::switches::kEnableChromeBrowserCloudManagement);
+  }
+#endif
+
+ private:
+  std::unique_ptr<policy::FakeBrowserDMTokenStorage> browser_dm_token_storage_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         EnterpriseReportingPrivateGetContextInfoBrowserTest,
+                         testing::Combine(testing::Bool(), testing::Bool()));
+
+IN_PROC_BROWSER_TEST_P(EnterpriseReportingPrivateGetContextInfoBrowserTest,
+                       AffiliationIDs) {
+  auto function =
+      base::MakeRefCounted<EnterpriseReportingPrivateGetContextInfoFunction>();
+  auto context_info_value = std::unique_ptr<base::Value>(
+      extension_function_test_utils::RunFunctionAndReturnSingleResult(
+          function.get(),
+          /*args*/ "[]", browser()));
+  ASSERT_TRUE(context_info_value.get());
+
+  enterprise_reporting_private::ContextInfo info;
+  ASSERT_TRUE(enterprise_reporting_private::ContextInfo::Populate(
+      *context_info_value, &info));
+
+  if (profile_managed()) {
+    EXPECT_EQ(2u, info.profile_affiliation_ids.size());
+    EXPECT_EQ(kProfileID1, info.profile_affiliation_ids[0]);
+    EXPECT_EQ(kProfileID2, info.profile_affiliation_ids[1]);
+  } else {
+    EXPECT_TRUE(info.profile_affiliation_ids.empty());
+  }
+
+  if (browser_managed()) {
+    EXPECT_EQ(2u, info.browser_affiliation_ids.size());
+    EXPECT_EQ(kBrowserID1, info.browser_affiliation_ids[0]);
+    EXPECT_EQ(kBrowserID2, info.browser_affiliation_ids[1]);
+  } else {
+    EXPECT_TRUE(info.browser_affiliation_ids.empty());
+  }
+
+  EXPECT_TRUE(info.on_file_attached_providers.empty());
+  EXPECT_TRUE(info.on_file_downloaded_providers.empty());
+  EXPECT_TRUE(info.on_bulk_data_entry_providers.empty());
+  EXPECT_EQ(enterprise_reporting_private::REALTIME_URL_CHECK_MODE_DISABLED,
+            info.realtime_url_check_mode);
+  EXPECT_TRUE(info.on_security_event_providers.empty());
+
+  // TODO(crbug.com/1169222): Change this assertion once this attribute is
+  // implemented, as it should be returned even when no special context applies
+  // to the browser.
+  EXPECT_EQ("", info.browser_version);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/gcm/gcm_apitest.cc b/chrome/browser/extensions/api/gcm/gcm_apitest.cc
index 4aed8ec..8985adc 100644
--- a/chrome/browser/extensions/api/gcm/gcm_apitest.cc
+++ b/chrome/browser/extensions/api/gcm/gcm_apitest.cc
@@ -266,7 +266,8 @@
   ResultCatcher incognito_catcher;
   incognito_catcher.RestrictToBrowserContext(profile()->GetPrimaryOTRProfile());
 
-  ASSERT_TRUE(RunExtensionTestIncognito("gcm/functions/incognito"));
+  ASSERT_TRUE(RunExtensionTest({.name = "gcm/functions/incognito"},
+                               {.allow_in_incognito = true}));
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   EXPECT_TRUE(incognito_catcher.GetNextResult()) << incognito_catcher.message();
diff --git a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
index 0b9d2807..39a7ebf 100644
--- a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
+++ b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
@@ -66,7 +66,8 @@
   ResultCatcher incognito_catcher;
   incognito_catcher.RestrictToBrowserContext(profile()->GetPrimaryOTRProfile());
 
-  ASSERT_TRUE(RunExtensionTestIncognito("instance_id/incognito"));
+  ASSERT_TRUE(RunExtensionTest({.name = "instance_id/incognito"},
+                               {.allow_in_incognito = true}));
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   EXPECT_TRUE(incognito_catcher.GetNextResult()) << incognito_catcher.message();
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
index 6244a7cc..c3fdd23 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -53,11 +53,9 @@
       public testing::WithParamInterface<ContextType> {
  protected:
   bool RunLazyTest(const std::string& extension_name) {
-    if (GetParam() == ContextType::kEventPage) {
-      return RunExtensionTest(extension_name);
-    }
-    return RunExtensionTestWithFlags(
-        extension_name, kFlagRunAsServiceWorkerBasedExtension, kFlagNone);
+    return RunExtensionTest(
+        {.name = extension_name.c_str()},
+        {.load_as_service_worker = GetParam() == ContextType::kServiceWorker});
   }
 };
 
diff --git a/chrome/browser/extensions/api/module/module_apitest.cc b/chrome/browser/extensions/api/module/module_apitest.cc
index 6d1831d..7b3af50 100644
--- a/chrome/browser/extensions/api/module/module_apitest.cc
+++ b/chrome/browser/extensions/api/module/module_apitest.cc
@@ -8,13 +8,15 @@
 using ExtensionModuleApiTest = extensions::ExtensionApiTest;
 
 IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoFile) {
-  ASSERT_TRUE(RunExtensionTestWithFileAccess("extension_module/cognito_file"))
+  ASSERT_TRUE(RunExtensionTest({.name = "extension_module/cognito_file"},
+                               {.allow_file_access = true}))
       << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoFile) {
-  ASSERT_TRUE(RunExtensionTestIncognitoWithFileAccess(
-      "extension_module/incognito_file"))
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "extension_module/incognito_file"},
+                       {.allow_in_incognito = true, .allow_file_access = true}))
       << message_;
 }
 
@@ -23,6 +25,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoNoFile) {
-  ASSERT_TRUE(RunExtensionTestIncognito("extension_module/incognito_nofile"))
+  ASSERT_TRUE(RunExtensionTest({.name = "extension_module/incognito_nofile"},
+                               {.allow_in_incognito = true}))
       << message_;
 }
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index 270e7a77..050730d 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -261,7 +261,9 @@
   ResultCatcher catcher_incognito;
   catcher_incognito.RestrictToBrowserContext(profile->GetPrimaryOTRProfile());
 
-  ASSERT_TRUE(RunExtensionTestIncognito("omnibox")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "omnibox"}, {.allow_in_incognito = true}))
+      << message_;
 
   // Open an incognito window and wait for the incognito extension process to
   // respond.
diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
index c8194d2..d08dadf 100644
--- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
@@ -206,7 +206,8 @@
 
   EXPECT_TRUE(RunExtensionTest("permissions/file_access_no")) << message_;
   EXPECT_FALSE(prefs->AllowFileAccess(last_loaded_extension_id()));
-  EXPECT_TRUE(RunExtensionTestWithFileAccess("permissions/file_access_yes"))
+  EXPECT_TRUE(RunExtensionTest({.name = "permissions/file_access_yes"},
+                               {.allow_file_access = true}))
       << message_;
   EXPECT_TRUE(prefs->AllowFileAccess(last_loaded_extension_id()));
 }
diff --git a/chrome/browser/extensions/api/preference/preference_apitest.cc b/chrome/browser/extensions/api/preference/preference_apitest.cc
index 009b85e..66a90dc 100644
--- a/chrome/browser/extensions/api/preference/preference_apitest.cc
+++ b/chrome/browser/extensions/api/preference/preference_apitest.cc
@@ -193,9 +193,9 @@
   PrefService* prefs = profile_->GetPrefs();
   SetCookieControlsMode(prefs, CookieControlsMode::kOff);
 
-  EXPECT_TRUE(
-      RunExtensionTestIncognito("preference/persistent_incognito")) <<
-      message_;
+  EXPECT_TRUE(RunExtensionTest({.name = "preference/persistent_incognito"},
+                               {.allow_in_incognito = true}))
+      << message_;
 
   // Setting an incognito preference should not create an incognito profile.
   EXPECT_FALSE(profile_->HasPrimaryOTRProfile());
@@ -221,9 +221,9 @@
   PrefService* prefs = profile_->GetPrefs();
   SetCookieControlsMode(prefs, CookieControlsMode::kOff);
 
-  EXPECT_TRUE(
-      RunExtensionTestIncognito("preference/session_only_incognito")) <<
-      message_;
+  EXPECT_TRUE(RunExtensionTest({.name = "preference/session_only_incognito"},
+                               {.allow_in_incognito = true}))
+      << message_;
 
   EXPECT_TRUE(profile_->HasPrimaryOTRProfile());
 
@@ -253,8 +253,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, OnChange) {
-  EXPECT_TRUE(RunExtensionTestIncognito("preference/onchange")) <<
-      message_;
+  EXPECT_TRUE(RunExtensionTest({.name = "preference/onchange"},
+                               {.allow_in_incognito = true}))
+      << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, OnChangeSplit) {
diff --git a/chrome/browser/extensions/api/proxy/proxy_apitest.cc b/chrome/browser/extensions/api/proxy/proxy_apitest.cc
index d34b516..c227b40 100644
--- a/chrome/browser/extensions/api/proxy/proxy_apitest.cc
+++ b/chrome/browser/extensions/api/proxy/proxy_apitest.cc
@@ -101,7 +101,9 @@
 
 // Tests direct connection settings.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyDirectSettings) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/direct")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/direct"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -119,7 +121,9 @@
 // Tests that proxy settings are changed appropriately when the extension is
 // disabled or enabled.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, SettingsChangeOnDisableEnable) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/direct")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/direct"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -138,7 +142,9 @@
 // Tests that proxy settings corresponding to an extension are removed when
 // the extension is uninstalled.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, SettingsRemovedOnUninstall) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/direct")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/direct"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -155,7 +161,9 @@
 // crbug.com/709264.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest,
                        PRE_SettingsRemovedOnPolicyBlocklist) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/direct")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/direct"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -194,7 +202,9 @@
 
 // Tests auto-detect settings.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyAutoSettings) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/auto")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/auto"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -285,7 +295,9 @@
 
 // Tests setting separate proxies for each scheme.
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyFixedIndividual) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/individual")) << message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "proxy/individual"},
+                               {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -314,8 +326,9 @@
 // Tests setting values only for incognito mode
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest,
                        ProxyFixedIndividualIncognitoOnly) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/individual_incognito_only")) <<
-      message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "proxy/individual_incognito_only"},
+                               {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -337,8 +350,9 @@
 // Tests setting values also for incognito mode
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest,
                        ProxyFixedIndividualIncognitoAlso) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/individual_incognito_also")) <<
-      message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "proxy/individual_incognito_also"},
+                               {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
@@ -376,7 +390,9 @@
 
 IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest,
     ProxyBypass) {
-  ASSERT_TRUE(RunExtensionTestIncognito("proxy/bypass")) << message_;
+  ASSERT_TRUE(
+      RunExtensionTest({.name = "proxy/bypass"}, {.allow_in_incognito = true}))
+      << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension);
 
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.cc b/chrome/browser/extensions/api/scripting/scripting_api.cc
index 335d17a..5ad3ca90 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -26,6 +26,53 @@
 namespace {
 
 constexpr char kCouldNotLoadFileError[] = "Could not load file: '*'.";
+constexpr char kExactlyOneOfCssAndFilesError[] =
+    "Exactly one of 'css' and 'files' must be specified.";
+
+// Note: CSS always injects as soon as possible, so we default to
+// document_start. Because of tab loading, there's no guarantee this will
+// *actually* inject before page load, but it will at least inject "soon".
+constexpr UserScript::RunLocation kCSSRunLocation = UserScript::DOCUMENT_START;
+
+// Converts the given `style_origin` to a CSSOrigin.
+CSSOrigin ConvertStyleOriginToCSSOrigin(
+    api::scripting::StyleOrigin style_origin) {
+  CSSOrigin css_origin = CSSOrigin::kAuthor;
+  switch (style_origin) {
+    case api::scripting::STYLE_ORIGIN_NONE:
+    case api::scripting::STYLE_ORIGIN_AUTHOR:
+      css_origin = CSSOrigin::kAuthor;
+      break;
+    case api::scripting::STYLE_ORIGIN_USER:
+      css_origin = CSSOrigin::kUser;
+      break;
+  }
+
+  return css_origin;
+}
+
+// Checks `files` and populates `resource_out` with the appropriate extension
+// resource. Returns true on success; on failure, populates `error_out`.
+bool GetFileResource(const std::vector<std::string>& files,
+                     const Extension& extension,
+                     ExtensionResource* resource_out,
+                     std::string* error_out) {
+  if (files.size() != 1) {
+    constexpr char kExactlyOneFileError[] =
+        "Exactly one file must be specified.";
+    *error_out = kExactlyOneFileError;
+    return false;
+  }
+  ExtensionResource resource = extension.GetResource(files[0]);
+  if (resource.extension_root().empty() || resource.relative_path().empty()) {
+    *error_out =
+        ErrorUtils::FormatErrorMessage(kCouldNotLoadFileError, files[0]);
+    return false;
+  }
+
+  *resource_out = std::move(resource);
+  return true;
+}
 
 // Returns true if the `permissions` allow for injection into the given `frame`.
 // If false, populates `error`.
@@ -160,17 +207,9 @@
                        bool requires_localization,
                        LoadAndLocalizeResourceCallback callback,
                        std::string* error) {
-  if (files.size() != 1) {
-    constexpr char kExactlyOneFileError[] =
-        "Exactly one file must be specified.";
-    *error = kExactlyOneFileError;
+  ExtensionResource resource;
+  if (!GetFileResource(files, extension, &resource, error))
     return false;
-  }
-  ExtensionResource resource = extension.GetResource(files[0]);
-  if (resource.extension_root().empty() || resource.relative_path().empty()) {
-    *error = ErrorUtils::FormatErrorMessage(kCouldNotLoadFileError, files[0]);
-    return false;
-  }
 
   LoadAndLocalizeResource(extension, resource, requires_localization,
                           std::move(callback));
@@ -259,7 +298,7 @@
       ScriptExecutor::MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
       ScriptExecutor::DEFAULT_PROCESS,
       /* webview_src */ GURL(), std::move(script_url), user_gesture(),
-      CSS_ORIGIN_AUTHOR, ScriptExecutor::JSON_SERIALIZED_RESULT,
+      CSSOrigin::kAuthor, ScriptExecutor::JSON_SERIALIZED_RESULT,
       base::BindOnce(&ScriptingExecuteScriptFunction::OnScriptExecuted, this));
 
   return true;
@@ -311,8 +350,7 @@
 
   if ((injection_.files && injection_.css) ||
       (!injection_.files && !injection_.css)) {
-    return RespondNow(
-        Error("Exactly one of 'css' and 'files' must be specified"));
+    return RespondNow(Error(kExactlyOneOfCssAndFilesError));
   }
 
   if (injection_.files) {
@@ -369,27 +407,13 @@
   }
   DCHECK(script_executor);
 
-  CSSOrigin origin = CSS_ORIGIN_AUTHOR;
-  switch (injection_.origin) {
-    case api::scripting::STYLE_ORIGIN_NONE:
-    case api::scripting::STYLE_ORIGIN_AUTHOR:
-      origin = CSS_ORIGIN_AUTHOR;
-      break;
-    case api::scripting::STYLE_ORIGIN_USER:
-      origin = CSS_ORIGIN_USER;
-      break;
-  }
-
-  // Note: CSS always injects as soon as possible, so we default to
-  // document_start. Because of tab loading, there's no guarantee this will
-  // *actually* inject before page load, but it will at least inject "soon".
-  constexpr UserScript::RunLocation kRunLocation = UserScript::DOCUMENT_START;
   script_executor->ExecuteScript(
       HostID(HostID::EXTENSIONS, extension()->id()), UserScript::ADD_CSS,
       std::move(code_to_execute), frame_scope, frame_ids,
-      ScriptExecutor::MATCH_ABOUT_BLANK, kRunLocation,
+      ScriptExecutor::MATCH_ABOUT_BLANK, kCSSRunLocation,
       ScriptExecutor::DEFAULT_PROCESS,
-      /* webview_src */ GURL(), std::move(script_url), user_gesture(), origin,
+      /* webview_src */ GURL(), std::move(script_url), user_gesture(),
+      ConvertStyleOriginToCSSOrigin(injection_.origin),
       ScriptExecutor::NO_RESULT,
       base::BindOnce(&ScriptingInsertCSSFunction::OnCSSInserted, this));
 
@@ -408,4 +432,72 @@
   Respond(NoArguments());
 }
 
+ScriptingRemoveCSSFunction::ScriptingRemoveCSSFunction() = default;
+ScriptingRemoveCSSFunction::~ScriptingRemoveCSSFunction() = default;
+
+ExtensionFunction::ResponseAction ScriptingRemoveCSSFunction::Run() {
+  std::unique_ptr<api::scripting::RemoveCSS::Params> params(
+      api::scripting::RemoveCSS::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  api::scripting::CSSInjection& injection = params->injection;
+
+  if ((injection.files && injection.css) ||
+      (!injection.files && !injection.css)) {
+    return RespondNow(Error(kExactlyOneOfCssAndFilesError));
+  }
+
+  GURL script_url;
+  std::string error;
+  std::string code;
+  if (injection.files) {
+    // Note: Since we're just removing the CSS, we don't actually need to load
+    // the file here. It's okay for `code` to be empty in this case.
+    ExtensionResource resource;
+    if (!GetFileResource(*injection.files, *extension(), &resource, &error))
+      return RespondNow(Error(std::move(error)));
+
+    script_url = extension()->GetResourceURL(injection.files->at(0));
+  } else {
+    DCHECK(injection.css);
+    code = std::move(*injection.css);
+  }
+
+  ScriptExecutor* script_executor = nullptr;
+  ScriptExecutor::FrameScope frame_scope = ScriptExecutor::SPECIFIED_FRAMES;
+  std::vector<int> frame_ids;
+  if (!CanAccessTarget(*extension()->permissions_data(), injection.target,
+                       browser_context(), include_incognito_information(),
+                       &script_executor, &frame_scope, &frame_ids, &error)) {
+    return RespondNow(Error(std::move(error)));
+  }
+  DCHECK(script_executor);
+
+  DCHECK(code.empty() || !script_url.is_valid());
+
+  script_executor->ExecuteScript(
+      HostID(HostID::EXTENSIONS, extension()->id()), UserScript::REMOVE_CSS,
+      std::move(code), frame_scope, frame_ids,
+      ScriptExecutor::MATCH_ABOUT_BLANK, kCSSRunLocation,
+      ScriptExecutor::DEFAULT_PROCESS,
+      /* webview_src */ GURL(), std::move(script_url), user_gesture(),
+      ConvertStyleOriginToCSSOrigin(injection.origin),
+      ScriptExecutor::NO_RESULT,
+      base::BindOnce(&ScriptingRemoveCSSFunction::OnCSSRemoved, this));
+
+  return RespondLater();
+}
+
+void ScriptingRemoveCSSFunction::OnCSSRemoved(
+    std::vector<ScriptExecutor::FrameResult> results) {
+  // If only a single frame was included and the injection failed, respond with
+  // an error.
+  if (results.size() == 1 && !results[0].error.empty()) {
+    Respond(Error(std::move(results[0].error)));
+    return;
+  }
+
+  Respond(NoArguments());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.h b/chrome/browser/extensions/api/scripting/scripting_api.h
index 947dc261..56669ab1 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.h
+++ b/chrome/browser/extensions/api/scripting/scripting_api.h
@@ -74,6 +74,25 @@
   api::scripting::CSSInjection injection_;
 };
 
+class ScriptingRemoveCSSFunction : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("scripting.removeCSS", SCRIPTING_REMOVECSS)
+
+  ScriptingRemoveCSSFunction();
+  ScriptingRemoveCSSFunction(const ScriptingRemoveCSSFunction&) = delete;
+  ScriptingRemoveCSSFunction& operator=(const ScriptingRemoveCSSFunction&) =
+      delete;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+ private:
+  ~ScriptingRemoveCSSFunction() override;
+
+  // Called when the CSS removal is complete.
+  void OnCSSRemoved(std::vector<ScriptExecutor::FrameResult> results);
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_SCRIPTING_SCRIPTING_API_H_
diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
index b7a2fd5..1cd9cfde 100644
--- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -66,7 +66,8 @@
   OpenURLInNewTab(
       embedded_test_server()->GetURL("chromium.org", "/title2.html"));
 
-  ASSERT_TRUE(RunExtensionTestIgnoreManifestWarnings("scripting/main_frame"))
+  ASSERT_TRUE(RunExtensionTest({.name = "scripting/main_frame"},
+                               {.ignore_manifest_warnings = true}))
       << message_;
 }
 
@@ -94,4 +95,8 @@
   ASSERT_TRUE(RunExtensionTest("scripting/css_injection")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ScriptingAPITest, CSSRemoval) {
+  ASSERT_TRUE(RunExtensionTest("scripting/remove_css")) << message_;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/search/search_api_apitest.cc b/chrome/browser/extensions/api/search/search_api_apitest.cc
index bb2d8a64..388e480 100644
--- a/chrome/browser/extensions/api/search/search_api_apitest.cc
+++ b/chrome/browser/extensions/api/search/search_api_apitest.cc
@@ -21,7 +21,9 @@
 IN_PROC_BROWSER_TEST_F(SearchApiTest, Incognito) {
   ResultCatcher catcher;
   CreateIncognitoBrowser(browser()->profile());
-  ASSERT_TRUE(RunExtensionTestIncognito("search/query/incognito")) << message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "search/query/incognito"},
+                               {.allow_in_incognito = true}))
+      << message_;
 }
 
 // Test incognito browser in extension split mode.
@@ -30,7 +32,8 @@
   catcher.RestrictToBrowserContext(
       browser()->profile()->GetPrimaryOTRProfile());
   CreateIncognitoBrowser(browser()->profile());
-  ASSERT_TRUE(RunExtensionTestIncognito("search/query/incognito_split"))
+  ASSERT_TRUE(RunExtensionTest({.name = "search/query/incognito_split"},
+                               {.allow_in_incognito = true}))
       << message_;
 }
 
diff --git a/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc b/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc
index dbaadf5..d3cc53b4 100644
--- a/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc
+++ b/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.h"
 
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/extensions/api/settings_private/generated_pref.h"
 #include "chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc b/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc
index 9eaea7c..8f404b82 100644
--- a/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc
+++ b/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.h"
 
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/extensions/api/settings_private/generated_pref.h"
 #include "chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
index 13c821c8..fbc2a3aa5 100644
--- a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
+++ b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h"
 
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/extensions/api/settings_private.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h
index 99ec2946..3ad6369 100644
--- a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h
+++ b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/extensions/api/settings_private/generated_pref.h"
 
 class Profile;
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 50f8070f..ec4c176 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -57,6 +57,7 @@
 #include "ash/public/cpp/ambient/ambient_prefs.h"
 #include "ash/public/cpp/ash_pref_names.h"  // nogncheck
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/full_restore/full_restore_prefs.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
@@ -66,7 +67,6 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/supervised_user_cros_settings_provider.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.h"
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index f15516d..3166295 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -219,14 +219,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ClientRedirect) {
-  ASSERT_TRUE(RunExtensionTest("webnavigation/clientRedirect"))
-      << message_;
+  ASSERT_TRUE(RunExtensionTest("webnavigation/clientRedirect")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ServerRedirect) {
   ASSERT_TRUE(StartEmbeddedTestServer());
-  ASSERT_TRUE(RunExtensionTest("webnavigation/serverRedirect"))
-      << message_;
+  ASSERT_TRUE(RunExtensionTest("webnavigation/serverRedirect")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, FormSubmission) {
@@ -253,8 +251,7 @@
   content::RenderProcessHost::SetMaxRendererProcessCount(1);
 
   // Wait for the extension to set itself up and return control to us.
-  ASSERT_TRUE(
-      RunExtensionTest("webnavigation/serverRedirectSingleProcess"))
+  ASSERT_TRUE(RunExtensionTest("webnavigation/serverRedirectSingleProcess"))
       << message_;
 
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
@@ -353,8 +350,7 @@
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, RequestOpenTab) {
 
   // Wait for the extension to set itself up and return control to us.
-  ASSERT_TRUE(RunExtensionTest("webnavigation/requestOpenTab"))
-      << message_;
+  ASSERT_TRUE(RunExtensionTest("webnavigation/requestOpenTab")) << message_;
 
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_TRUE(content::WaitForLoadStop(tab));
@@ -424,7 +420,8 @@
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   // Wait for the extension to set itself up and return control to us.
-  ASSERT_TRUE(RunExtensionTestIncognito("webnavigation/targetBlank"))
+  ASSERT_TRUE(RunExtensionTest({.name = "webnavigation/targetBlank"},
+                               {.allow_in_incognito = true}))
       << message_;
 
   ResultCatcher catcher;
diff --git a/chrome/browser/extensions/chrome_test_extension_loader.cc b/chrome/browser/extensions/chrome_test_extension_loader.cc
index c10382a..6c6882f 100644
--- a/chrome/browser/extensions/chrome_test_extension_loader.cc
+++ b/chrome/browser/extensions/chrome_test_extension_loader.cc
@@ -21,11 +21,11 @@
 #include "extensions/browser/extension_creator.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_user_script_manager.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/user_script_loader.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/manifest_handlers/content_scripts_handler.h"
 #include "extensions/common/manifest_handlers/incognito_info.h"
@@ -125,12 +125,13 @@
 
 bool ChromeTestExtensionLoader::WaitForExtensionReady(
     const Extension& extension) {
-  ExtensionUserScriptManager* user_script_manager =
-      ExtensionSystem::Get(browser_context_)->extension_user_script_manager();
+  UserScriptManager* user_script_manager =
+      ExtensionSystem::Get(browser_context_)->user_script_manager();
   // Note: |user_script_manager| can be null in tests.
   if (user_script_manager &&
       !ContentScriptsInfo::GetContentScripts(&extension).empty()) {
-    UserScriptLoader* user_script_loader = user_script_manager->script_loader();
+    UserScriptLoader* user_script_loader =
+        user_script_manager->manifest_script_loader();
     HostID host_id(HostID::EXTENSIONS, extension_id_);
     if (!user_script_loader->HasLoadedScripts(host_id)) {
       ContentScriptLoadWaiter waiter(user_script_loader);
diff --git a/chrome/browser/extensions/chrome_test_extension_loader_browsertest.cc b/chrome/browser/extensions/chrome_test_extension_loader_browsertest.cc
index fc10b37..061f3ffc 100644
--- a/chrome/browser/extensions/chrome_test_extension_loader_browsertest.cc
+++ b/chrome/browser/extensions/chrome_test_extension_loader_browsertest.cc
@@ -14,8 +14,8 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_user_script_manager.h"
 #include "extensions/browser/user_script_loader.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/host_id.h"
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
@@ -80,8 +80,8 @@
 
   ExtensionSystem* extension_system = ExtensionSystem::Get(profile());
   EXPECT_TRUE(
-      extension_system->extension_user_script_manager()
-          ->script_loader()
+      extension_system->user_script_manager()
+          ->manifest_script_loader()
           ->HasLoadedScripts(HostID(HostID::EXTENSIONS, extension->id())));
 
   // Sanity check: Test that the scripts inject.
diff --git a/chrome/browser/extensions/cross_origin_xhr_apitest.cc b/chrome/browser/extensions/cross_origin_xhr_apitest.cc
index 0b08e41..8bafd616 100644
--- a/chrome/browser/extensions/cross_origin_xhr_apitest.cc
+++ b/chrome/browser/extensions/cross_origin_xhr_apitest.cc
@@ -32,7 +32,8 @@
 // "<all_urls>" host permissions.
 IN_PROC_BROWSER_TEST_F(CrossOriginXHR, FileAccessAllURLs) {
   ASSERT_TRUE(
-      RunExtensionTestWithFileAccess("cross_origin_xhr/file_access_all_urls"))
+      RunExtensionTest({.name = "cross_origin_xhr/file_access_all_urls"},
+                       {.allow_file_access = true}))
       << message_;
 }
 
@@ -47,6 +48,7 @@
 // permissions to the file scheme even though it has file access.
 IN_PROC_BROWSER_TEST_F(CrossOriginXHR, FileAccessNoHosts) {
   ASSERT_TRUE(
-      RunExtensionTestWithFileAccess("cross_origin_xhr/file_access_no_hosts"))
+      RunExtensionTest({.name = "cross_origin_xhr/file_access_no_hosts"},
+                       {.allow_file_access = true}))
       << message_;
 }
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index 6ee1054..74ed5274 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -86,9 +86,88 @@
   test_config_.reset(NULL);
 }
 
+bool ExtensionApiTest::RunExtensionTest(const RunOptions& run_options) {
+  return RunExtensionTest(run_options, {});
+}
+
 bool ExtensionApiTest::RunExtensionTest(const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr, kFlagNone,
-                              kFlagNone);
+  return RunExtensionTest({.name = extension_name.c_str()}, {});
+}
+
+bool ExtensionApiTest::RunExtensionTest(const RunOptions& run_options,
+                                        const LoadOptions& load_options) {
+  // Do some sanity checks for options that are mutually exclusive or
+  // only valid with other options.
+  CHECK(run_options.name || run_options.page_url)
+      << "Must specify either 'name' or 'page_url'";
+  CHECK(!(run_options.extension_url && run_options.page_url))
+      << "'extension_url' and 'page_url' are mutually exclusive.";
+  CHECK(!run_options.open_in_incognito || run_options.page_url)
+      << "'open_in_incognito' is only allowed if specifiying 'page_url'";
+  CHECK(!(run_options.launch_as_platform_app && run_options.page_url))
+      << "'launch_as_platform_app' and 'page_url' are mutually exclusive.";
+
+  if (run_options.custom_arg)
+    SetCustomArg(run_options.custom_arg);
+
+  ResultCatcher catcher;
+
+  const Extension* extension = nullptr;
+  if (run_options.name) {
+    const base::FilePath& root_path = run_options.use_extensions_root_dir
+                                          ? shared_test_data_dir_
+                                          : test_data_dir_;
+    base::FilePath extension_path = root_path.AppendASCII(run_options.name);
+    // TODO(https://crbug.com/1171429): Move load_as_component into LoadOptions
+    // and unify LoadExtension and LoadExtensionAsComponent.
+    // As it stands today, all LoadOptions will be ignored when loading the
+    // extension as a component extension.
+    if (run_options.load_as_component) {
+      extension = LoadExtensionAsComponent(extension_path);
+    } else {
+      extension = LoadExtension(extension_path, load_options);
+    }
+    if (!extension) {
+      message_ = "Failed to load extension.";
+      return false;
+    }
+  }
+
+  // If there is a page_url to load, navigate it.
+  // TODO(https://crbug.com/1171429): Separate page_url into page_url and
+  // extension_url.
+  if (run_options.page_url) {
+    GURL url(run_options.page_url);
+
+    // Note: We use is_valid() here in the expectation that the provided url
+    // may lack a scheme & host and thus be a relative url within the loaded
+    // extension.
+    if (!url.is_valid()) {
+      DCHECK(run_options.name) << "Relative page_url given with no name";
+
+      url = extension->GetResourceURL(run_options.page_url);
+    }
+
+    if (run_options.open_in_incognito)
+      OpenURLOffTheRecord(browser()->profile(), url);
+    else
+      ui_test_utils::NavigateToURL(browser(), url);
+  } else if (run_options.launch_as_platform_app) {
+    apps::AppLaunchParams params(
+        extension->id(), LaunchContainer::kLaunchContainerNone,
+        WindowOpenDisposition::NEW_WINDOW, AppLaunchSource::kSourceTest);
+    params.command_line = *base::CommandLine::ForCurrentProcess();
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        ->LaunchAppWithParams(std::move(params));
+  }
+
+  if (!catcher.GetNextResult()) {
+    message_ = catcher.message();
+    return false;
+  }
+
+  return true;
 }
 
 bool ExtensionApiTest::RunExtensionTestWithFlags(
@@ -115,24 +194,6 @@
                               browser_test_flags, api_test_flags);
 }
 
-bool ExtensionApiTest::RunExtensionTestIncognito(
-    const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr,
-                              kFlagEnableIncognito, kFlagNone);
-}
-
-bool ExtensionApiTest::RunExtensionTestIgnoreManifestWarnings(
-    const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr,
-                              kFlagIgnoreManifestWarnings, kFlagNone);
-}
-
-bool ExtensionApiTest::RunExtensionTestAllowOldManifestVersion(
-    const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr,
-                              kFlagAllowOldManifestVersions, kFlagNone);
-}
-
 bool ExtensionApiTest::RunComponentExtensionTest(
     const std::string& extension_name) {
   return RunExtensionTestImpl(extension_name, std::string(), nullptr, kFlagNone,
@@ -146,19 +207,6 @@
                               kFlagNone, kFlagLoadAsComponent);
 }
 
-bool ExtensionApiTest::RunExtensionTestWithFileAccess(
-    const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr,
-                              kFlagEnableFileAccess, kFlagNone);
-}
-
-bool ExtensionApiTest::RunExtensionTestIncognitoWithFileAccess(
-    const std::string& extension_name) {
-  return RunExtensionTestImpl(extension_name, std::string(), nullptr,
-                              kFlagEnableFileAccess | kFlagEnableIncognito,
-                              kFlagNone);
-}
-
 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
                                            const std::string& page_url) {
   return RunExtensionSubtestWithArgAndFlags(extension_name, page_url, nullptr,
diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h
index e6f1b52..04887fe 100644
--- a/chrome/browser/extensions/extension_apitest.h
+++ b/chrome/browser/extensions/extension_apitest.h
@@ -52,6 +52,38 @@
     kFlagUseRootExtensionsDir = ExtensionBrowserTest::kFlagNextValue << 3,
   };
 
+  struct RunOptions {
+    // Load the specified extension for the test. This is a subdirectory
+    // in "chrome/test/data/extensions/api_test".
+    const char* name = nullptr;
+
+    // Start the test by opening the specified page URL. This must be an
+    // absolute URL.
+    const char* page_url = nullptr;
+
+    // Start the test by opening the specified extension URL. This is treated
+    // as a relative path to an extension resource.
+    const char* extension_url = nullptr;
+
+    // The custom arg to be passed into the test.
+    const char* custom_arg = nullptr;
+
+    // Launch the test page in an incognito window.
+    bool open_in_incognito = false;
+
+    // TODO(https://crbug.com/1171429): Move to load options and
+    // refactor implementation into ExtensionBrowserTest.
+    // Loads the extension with location COMPONENT.
+    bool load_as_component = false;
+
+    // Launch the extension as a platform app.
+    bool launch_as_platform_app = false;
+
+    // Use //extensions/test/data/ as the root path instead of the default
+    // path of //chrome/test/data/extensions/api_test/.
+    bool use_extensions_root_dir = false;
+  };
+
   ExtensionApiTest();
   ~ExtensionApiTest() override;
 
@@ -60,8 +92,13 @@
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
-  // Loads |extension_name| and waits for pass / fail notification.
-  // |extension_name| is a directory in "chrome/test/data/extensions/api_test".
+  bool RunExtensionTest(const RunOptions& run_options,
+                        const LoadOptions& load_options) WARN_UNUSED_RESULT;
+
+  bool RunExtensionTest(const RunOptions& run_options) WARN_UNUSED_RESULT;
+
+  // Loads the extension with |extension_name| and default RunOptions and
+  // LoadOptions
   bool RunExtensionTest(const std::string& extension_name) WARN_UNUSED_RESULT;
 
   // Same as RunExtensionTest, except run with the specific |flags| (as defined
@@ -83,18 +120,6 @@
                                        int browser_test_flags,
                                        int api_test_flags) WARN_UNUSED_RESULT;
 
-  // Same as RunExtensionTest, but enables the extension for incognito mode.
-  bool RunExtensionTestIncognito(const std::string& extension_name)
-      WARN_UNUSED_RESULT;
-
-  // Same as RunExtensionTest, but ignores any warnings in the manifest.
-  bool RunExtensionTestIgnoreManifestWarnings(const std::string& extension_name)
-      WARN_UNUSED_RESULT;
-
-  // Same as RunExtensionTest, allow old manifest ersions.
-  bool RunExtensionTestAllowOldManifestVersion(
-      const std::string& extension_name) WARN_UNUSED_RESULT;
-
   // Same as RunExtensionTest, but loads extension as component.
   bool RunComponentExtensionTest(const std::string& extension_name)
       WARN_UNUSED_RESULT;
@@ -104,14 +129,6 @@
                                         const char* custom_arg)
       WARN_UNUSED_RESULT;
 
-  // Same as RunExtensionTest, but enables file access.
-  bool RunExtensionTestWithFileAccess(const std::string& extension_name)
-      WARN_UNUSED_RESULT;
-
-  // Same as RunExtensionTestIncognito, but enables file access.
-  bool RunExtensionTestIncognitoWithFileAccess(
-      const std::string& extension_name) WARN_UNUSED_RESULT;
-
   // If not empty, Load |extension_name|, load |page_url| and wait for pass /
   // fail notification from the extension API on the page. Note that if
   // |page_url| is not a valid url, it will be treated as a resource within
diff --git a/chrome/browser/extensions/extension_incognito_apitest.cc b/chrome/browser/extensions/extension_incognito_apitest.cc
index 16d4750c..f2614c9e 100644
--- a/chrome/browser/extensions/extension_incognito_apitest.cc
+++ b/chrome/browser/extensions/extension_incognito_apitest.cc
@@ -91,8 +91,9 @@
 // accidentally create an incognito profile.
 IN_PROC_BROWSER_TEST_F(IncognitoApiTest, DontCreateIncognitoProfile) {
   ASSERT_FALSE(browser()->profile()->HasPrimaryOTRProfile());
-  ASSERT_TRUE(RunExtensionTestIncognito(
-      "incognito/dont_create_profile")) << message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "incognito/dont_create_profile"},
+                               {.allow_in_incognito = true}))
+      << message_;
   ASSERT_FALSE(browser()->profile()->HasPrimaryOTRProfile());
 }
 
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index 218a39d..0ce3c0c 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -532,8 +532,7 @@
 
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
 
-  ASSERT_TRUE(RunExtensionTest("keybinding/basics"))
-      << message_;
+  ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_;
 
   CommandService* command_service = CommandService::Get(browser()->profile());
 
@@ -1029,10 +1028,9 @@
 
   bool is_incognito_enabled = GetParam();
 
-  if (is_incognito_enabled)
-    ASSERT_TRUE(RunExtensionTestIncognito("keybinding/basics")) << message_;
-  else
-    ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_;
+  ASSERT_TRUE(RunExtensionTest({.name = "keybinding/basics"},
+                               {.allow_in_incognito = is_incognito_enabled}))
+      << message_;
 
   // Open incognito window and navigate to test page.
   Browser* incognito_browser = OpenURLOffTheRecord(
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index c2f75b6..4c45066 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -847,13 +847,16 @@
                                scripts[0]->js_scripts()[0]->relative_path());
   base::FilePath expected_path =
       base::MakeAbsoluteFilePath(extension->path().AppendASCII("script1.js"));
-  EXPECT_TRUE(resource00.ComparePathWithDefault(expected_path));
+
+  EXPECT_EQ(expected_path.NormalizePathSeparators(),
+            resource00.GetFilePath().NormalizePathSeparators());
   ExtensionResource resource01(extension->id(),
                                scripts[0]->js_scripts()[1]->extension_root(),
                                scripts[0]->js_scripts()[1]->relative_path());
   expected_path =
       base::MakeAbsoluteFilePath(extension->path().AppendASCII("script2.js"));
-  EXPECT_TRUE(resource01.ComparePathWithDefault(expected_path));
+  EXPECT_EQ(expected_path.NormalizePathSeparators(),
+            resource01.GetFilePath().NormalizePathSeparators());
   EXPECT_EQ(1u, scripts[1]->url_patterns().patterns().size());
   EXPECT_EQ("http://*.news.com/*",
             scripts[1]->url_patterns().begin()->GetAsString());
@@ -863,7 +866,8 @@
   expected_path =
       extension->path().AppendASCII("js_files").AppendASCII("script3.js");
   expected_path = base::MakeAbsoluteFilePath(expected_path);
-  EXPECT_TRUE(resource10.ComparePathWithDefault(expected_path));
+  EXPECT_EQ(expected_path.NormalizePathSeparators(),
+            resource10.GetFilePath().NormalizePathSeparators());
 
   expected_patterns.ClearPatterns();
   AddPattern(&expected_patterns, "http://*.google.com/*");
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index 0a78f45..fe8e19ec 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -36,8 +36,8 @@
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_user_script_manager.h"
 #include "extensions/browser/user_script_loader.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/feature_switch.h"
@@ -151,11 +151,11 @@
             ->extension_service();
     ASSERT_EQ(expect_extensions_enabled, service->extensions_enabled());
 
-    extensions::ExtensionUserScriptManager* manager =
+    extensions::UserScriptManager* manager =
         extensions::ExtensionSystem::Get(browser()->profile())
-            ->extension_user_script_manager();
+            ->user_script_manager();
 
-    extensions::UserScriptLoader* loader = manager->script_loader();
+    extensions::UserScriptLoader* loader = manager->manifest_script_loader();
     if (!loader->initial_load_complete())
       extensions::ContentScriptLoadWaiter(loader).Wait();
     ASSERT_TRUE(loader->initial_load_complete());
@@ -237,11 +237,11 @@
       extension_list.push_back(it->get());
   }
 
-  extensions::ExtensionUserScriptManager* manager =
+  extensions::UserScriptManager* manager =
       extensions::ExtensionSystem::Get(browser()->profile())
-          ->extension_user_script_manager();
+          ->user_script_manager();
 
-  extensions::UserScriptLoader* loader = manager->script_loader();
+  extensions::UserScriptLoader* loader = manager->manifest_script_loader();
 
   for (size_t i = 0; i < extension_list.size(); ++i) {
     extensions::ContentScriptLoadWaiter waiter(loader);
diff --git a/chrome/browser/extensions/extension_system_factory.cc b/chrome/browser/extensions/extension_system_factory.cc
index 6306356..ccf132f9 100644
--- a/chrome/browser/extensions/extension_system_factory.cc
+++ b/chrome/browser/extensions/extension_system_factory.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/declarative_user_script_manager_factory.h"
 #include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registry_factory.h"
@@ -51,7 +50,6 @@
   DependsOn(ProcessManagerFactory::GetInstance());
   DependsOn(RendererStartupHelperFactory::GetInstance());
   DependsOn(BlocklistFactory::GetInstance());
-  DependsOn(DeclarativeUserScriptManagerFactory::GetInstance());
   DependsOn(EventRouterFactory::GetInstance());
   // This depends on ExtensionDownloader, which depends on
   // IdentityManager for webstore authentication.
diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc
index 22a91233..dec68dc2 100644
--- a/chrome/browser/extensions/extension_system_impl.cc
+++ b/chrome/browser/extensions/extension_system_impl.cc
@@ -50,7 +50,6 @@
 #include "extensions/browser/extension_pref_value_map_factory.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_user_script_manager.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/quota_service.h"
@@ -58,6 +57,7 @@
 #include "extensions/browser/service_worker_manager.h"
 #include "extensions/browser/state_store.h"
 #include "extensions/browser/updater/uninstall_ping_sender.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/browser/value_store/value_store_factory_impl.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/features/feature_channel.h"
@@ -201,8 +201,7 @@
 
   service_worker_manager_.reset(new ServiceWorkerManager(profile_));
 
-  extension_user_script_manager_ =
-      std::make_unique<ExtensionUserScriptManager>(profile_);
+  user_script_manager_ = std::make_unique<UserScriptManager>(profile_);
 
   // ExtensionService depends on RuntimeData.
   runtime_data_.reset(new RuntimeData(ExtensionRegistry::Get(profile_)));
@@ -334,9 +333,8 @@
   return management_policy_.get();
 }
 
-ExtensionUserScriptManager*
-ExtensionSystemImpl::Shared::extension_user_script_manager() {
-  return extension_user_script_manager_.get();
+UserScriptManager* ExtensionSystemImpl::Shared::user_script_manager() {
+  return user_script_manager_.get();
 }
 
 InfoMap* ExtensionSystemImpl::Shared::info_map() {
@@ -379,7 +377,7 @@
 void ExtensionSystemImpl::InitForRegularProfile(bool extensions_enabled) {
   TRACE_EVENT0("browser,startup", "ExtensionSystemImpl::InitForRegularProfile");
 
-  if (extension_user_script_manager() || extension_service())
+  if (user_script_manager() || extension_service())
     return;  // Already initialized.
 
   // The InfoMap needs to be created before the ProcessManager.
@@ -403,9 +401,8 @@
   return shared_->service_worker_manager();
 }
 
-ExtensionUserScriptManager*
-ExtensionSystemImpl::extension_user_script_manager() {
-  return shared_->extension_user_script_manager();
+UserScriptManager* ExtensionSystemImpl::user_script_manager() {
+  return shared_->user_script_manager();
 }
 
 StateStore* ExtensionSystemImpl::state_store() {
diff --git a/chrome/browser/extensions/extension_system_impl.h b/chrome/browser/extensions/extension_system_impl.h
index c6ba543..e7ad220 100644
--- a/chrome/browser/extensions/extension_system_impl.h
+++ b/chrome/browser/extensions/extension_system_impl.h
@@ -55,8 +55,7 @@
   RuntimeData* runtime_data() override;            // shared
   ManagementPolicy* management_policy() override;  // shared
   ServiceWorkerManager* service_worker_manager() override;  // shared
-  ExtensionUserScriptManager* extension_user_script_manager()
-      override;                                                    // shared
+  UserScriptManager* user_script_manager() override;        // shared
   StateStore* state_store() override;                              // shared
   StateStore* rules_store() override;                              // shared
   scoped_refptr<ValueStoreFactory> store_factory() override;       // shared
@@ -115,7 +114,7 @@
     RuntimeData* runtime_data();
     ManagementPolicy* management_policy();
     ServiceWorkerManager* service_worker_manager();
-    ExtensionUserScriptManager* extension_user_script_manager();
+    UserScriptManager* user_script_manager();
     InfoMap* info_map();
     QuotaService* quota_service();
     AppSorting* app_sorting();
@@ -137,7 +136,7 @@
     std::unique_ptr<ServiceWorkerManager> service_worker_manager_;
     // Shared memory region manager for scripts statically declared in extension
     // manifests. This region is shared between all extensions.
-    std::unique_ptr<ExtensionUserScriptManager> extension_user_script_manager_;
+    std::unique_ptr<UserScriptManager> user_script_manager_;
     std::unique_ptr<RuntimeData> runtime_data_;
     // ExtensionService depends on StateStore, Blocklist and RuntimeData.
     std::unique_ptr<ExtensionService> extension_service_;
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index a7b95c1..223bfa32 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -36,10 +36,12 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/notification_types.h"
+#include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/common/cors_util.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_handlers/permissions_parser.h"
+#include "extensions/common/mojom/renderer.mojom.h"
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/permissions/permissions_data.h"
 
@@ -672,18 +674,22 @@
     const URLPatternSet default_runtime_allowed_hosts) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
 
-  ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params params;
-  params.default_policy_blocked_hosts = default_runtime_blocked_hosts.Clone();
-  params.default_policy_allowed_hosts = default_runtime_allowed_hosts.Clone();
-
   // Send the new policy to the renderers.
   for (RenderProcessHost::iterator host_iterator(
            RenderProcessHost::AllHostsIterator());
        !host_iterator.IsAtEnd(); host_iterator.Advance()) {
     RenderProcessHost* host = host_iterator.GetCurrentValue();
-    if (profile->IsSameOrParent(
+    if (host->IsInitializedAndNotDead() &&
+        profile->IsSameOrParent(
             Profile::FromBrowserContext(host->GetBrowserContext()))) {
-      host->Send(new ExtensionMsg_UpdateDefaultPolicyHostRestrictions(params));
+      mojom::Renderer* renderer =
+          RendererStartupHelperFactory::GetForBrowserContext(
+              host->GetBrowserContext())
+              ->GetRenderer(host);
+      if (renderer) {
+        renderer->UpdateDefaultPolicyHostRestrictions(
+            default_runtime_blocked_hosts, default_runtime_allowed_hosts);
+      }
     }
   }
 }
diff --git a/chrome/browser/extensions/permissions_updater.h b/chrome/browser/extensions/permissions_updater.h
index 8be23beb..217ca019 100644
--- a/chrome/browser/extensions/permissions_updater.h
+++ b/chrome/browser/extensions/permissions_updater.h
@@ -190,8 +190,7 @@
 
   // Issues the relevant events, messages and notifications when the default
   // scope management policy have changed.
-  // Specifically, this sends the ExtensionMsg_UpdateDefaultHostRestrictions
-  // IPC message.
+  // Specifically, this sends the UpdateDefaultHostRestrictions Mojo message.
   static void NotifyDefaultPolicyHostRestrictionsUpdated(
       content::BrowserContext* browser_context,
       const URLPatternSet default_runtime_blocked_hosts,
diff --git a/chrome/browser/extensions/script_executor_browsertest.cc b/chrome/browser/extensions/script_executor_browsertest.cc
index 31184ebba..c4db58f 100644
--- a/chrome/browser/extensions/script_executor_browsertest.cc
+++ b/chrome/browser/extensions/script_executor_browsertest.cc
@@ -129,7 +129,7 @@
       {ExtensionApiFrameIdMap::kTopFrameId},
       ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
       ScriptExecutor::DEFAULT_PROCESS, GURL() /* webview_src */,
-      GURL() /* script_url */, false /* user_gesture */, CSS_ORIGIN_AUTHOR,
+      GURL() /* script_url */, false /* user_gesture */, CSSOrigin::kAuthor,
       ScriptExecutor::JSON_SERIALIZED_RESULT, helper.GetCallback());
   helper.Wait();
   EXPECT_EQ("New Title", base::UTF16ToUTF8(web_contents->GetTitle()));
@@ -209,7 +209,7 @@
         kCode, ScriptExecutor::SPECIFIED_FRAMES, {frame1_id, frame2_id},
         ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
         ScriptExecutor::DEFAULT_PROCESS, GURL() /* webview_src */,
-        GURL() /* script_url */, false /* user_gesture */, CSS_ORIGIN_AUTHOR,
+        GURL() /* script_url */, false /* user_gesture */, CSSOrigin::kAuthor,
         ScriptExecutor::JSON_SERIALIZED_RESULT, helper.GetCallback());
     helper.Wait();
 
@@ -228,7 +228,7 @@
         kCode, ScriptExecutor::INCLUDE_SUB_FRAMES, {frame1_id, frame2_id},
         ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
         ScriptExecutor::DEFAULT_PROCESS, GURL() /* webview_src */,
-        GURL() /* script_url */, false /* user_gesture */, CSS_ORIGIN_AUTHOR,
+        GURL() /* script_url */, false /* user_gesture */, CSSOrigin::kAuthor,
         ScriptExecutor::JSON_SERIALIZED_RESULT, helper.GetCallback());
     helper.Wait();
 
@@ -257,7 +257,7 @@
         {frame1_id, frame2_id, kNonExistentFrameId},
         ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
         ScriptExecutor::DEFAULT_PROCESS, GURL() /* webview_src */,
-        GURL() /* script_url */, false /* user_gesture */, CSS_ORIGIN_AUTHOR,
+        GURL() /* script_url */, false /* user_gesture */, CSSOrigin::kAuthor,
         ScriptExecutor::JSON_SERIALIZED_RESULT, helper.GetCallback());
     helper.Wait();
 
@@ -279,7 +279,7 @@
         kCode, ScriptExecutor::SPECIFIED_FRAMES, {kNonExistentFrameId},
         ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
         ScriptExecutor::DEFAULT_PROCESS, GURL() /* webview_src */,
-        GURL() /* script_url */, false /* user_gesture */, CSS_ORIGIN_AUTHOR,
+        GURL() /* script_url */, false /* user_gesture */, CSSOrigin::kAuthor,
         ScriptExecutor::JSON_SERIALIZED_RESULT, helper.GetCallback());
     helper.Wait();
 
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index 4691b3e..f4275e3 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -30,6 +30,7 @@
 #include "extensions/browser/quota_service.h"
 #include "extensions/browser/runtime_data.h"
 #include "extensions/browser/state_store.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/browser/value_store/test_value_store_factory.h"
 #include "extensions/browser/value_store/testing_value_store.h"
 #include "services/data_decoder/data_decoder_service.h"
@@ -89,6 +90,10 @@
   return extension_service_.get();
 }
 
+void TestExtensionSystem::CreateUserScriptManager() {
+  user_script_manager_ = std::make_unique<UserScriptManager>(profile_);
+}
+
 ExtensionService* TestExtensionSystem::extension_service() {
   return extension_service_.get();
 }
@@ -109,9 +114,8 @@
   return nullptr;
 }
 
-ExtensionUserScriptManager*
-TestExtensionSystem::extension_user_script_manager() {
-  return nullptr;
+UserScriptManager* TestExtensionSystem::user_script_manager() {
+  return user_script_manager_.get();
 }
 
 StateStore* TestExtensionSystem::state_store() {
diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h
index 9fa72e5..70d51a0 100644
--- a/chrome/browser/extensions/test_extension_system.h
+++ b/chrome/browser/extensions/test_extension_system.h
@@ -52,13 +52,16 @@
 
   void CreateSocketManager();
 
+  // Creates a UserScriptManager initialized with the testing profile,
+  void CreateUserScriptManager();
+
   void InitForRegularProfile(bool extensions_enabled) override {}
   void SetExtensionService(ExtensionService* service);
   ExtensionService* extension_service() override;
   RuntimeData* runtime_data() override;
   ManagementPolicy* management_policy() override;
   ServiceWorkerManager* service_worker_manager() override;
-  ExtensionUserScriptManager* extension_user_script_manager() override;
+  UserScriptManager* user_script_manager() override;
   StateStore* state_store() override;
   StateStore* rules_store() override;
   scoped_refptr<ValueStoreFactory> store_factory() override;
@@ -107,6 +110,7 @@
   scoped_refptr<InfoMap> info_map_;
   std::unique_ptr<QuotaService> quota_service_;
   std::unique_ptr<AppSorting> app_sorting_;
+  std::unique_ptr<UserScriptManager> user_script_manager_;
   base::OneShotEvent ready_;
 
   std::unique_ptr<data_decoder::test::InProcessDataDecoder>
diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc
index da9f6f17..565803b 100644
--- a/chrome/browser/extensions/user_script_listener.cc
+++ b/chrome/browser/extensions/user_script_listener.cc
@@ -16,7 +16,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/notification_service.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_user_script_manager.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/content_scripts_handler.h"
 #include "extensions/common/url_pattern.h"
@@ -203,11 +203,12 @@
       DCHECK(!extension_registry_observer_.IsObserving(registry));
       extension_registry_observer_.Add(registry);
 
-      ExtensionUserScriptManager* user_script_manager =
-          ExtensionSystem::Get(profile)->extension_user_script_manager();
+      UserScriptManager* user_script_manager =
+          ExtensionSystem::Get(profile)->user_script_manager();
       // Note: |user_script_manager| can be null in some tests.
       if (user_script_manager) {
-        UserScriptLoader* loader = user_script_manager->script_loader();
+        UserScriptLoader* loader =
+            user_script_manager->manifest_script_loader();
         DCHECK(!user_script_loader_observer_.IsObserving(loader));
         user_script_loader_observer_.Add(loader);
       }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index fb7e0fb..d7144e5 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -155,6 +155,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "arc-enable-usap",
+    "owners": [ "camurcu" ],
+    "expiry_milestone": 92
+  },
+  {
     "name": "arc-file-picker-experiment",
     "owners": [ "niwa" ],
     "expiry_milestone": 95
@@ -1607,11 +1612,6 @@
     "expiry_milestone": 89
   },
   {
-    "name": "enable-experimental-accessibility-cursor-colors",
-    "owners": [ "katie" ],
-    "expiry_milestone": 87
-  },
-  {
     "name": "enable-experimental-accessibility-dictation-extension",
     "owners": [ "akihiroota" ],
     "expiry_milestone": 92
@@ -2055,11 +2055,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "enable-on-device-assistant",
-    "owners": [ "croissant-eng" ],
-    "expiry_milestone": 88
-  },
-  {
     "name": "enable-oop-print-drivers",
     "owners": [ "awscreen", "thestig" ],
     "expiry_milestone": 92
@@ -2101,6 +2096,11 @@
     // it can badly break pages under test.
     "expiry_milestone": -1
   },
+  {
+    "name": "enable-pci-guard-ui",
+    "owners": [ "jimmyxgong", "cros-peripherals@google.com" ],
+    "expiry_milestone": 94
+  },
   { "name": "enable-phone-hub",
     "owners": [ "better-together-dev@google.com", "tjohnsonkanu", "khorimoto" ],
     "expiry_milestone": 90
@@ -2797,7 +2797,7 @@
   },
   {
     "name": "focus-mode",
-    "owners": [ "dfried", "pbos", "yiningwang@google.com" ],
+    "owners": [ "dfried", "pbos" ],
     "expiry_milestone": 82
   },
   {
@@ -3244,11 +3244,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "launcher-settings-search",
-    "owners": [ "jiameng", "tby" ],
-    "expiry_milestone": 88
-  },
-  {
     "name": "legacy-tls-enforced",
     "owners": [ "cthomp" ],
     "expiry_milestone": 92
@@ -4074,6 +4069,11 @@
     "expiry_milestone": 100
   },
   {
+    "name": "preemtive-link-to-text-generation",
+    "owners": [ "cheickcisse@google.com" ],
+    "expiry_milestone": 92
+  },
+  {
     "name": "prefer-constant-frame-rate",
     "owners": [ "chromeos-camera-eng@google.com" ],
     "expiry_milestone": 96
@@ -4362,7 +4362,7 @@
       "bdea",
       "chrome-safebrowsing-core@google.com"
     ],
-    "expiry_milestone": 88
+    "expiry_milestone": 91
   },
   {
     "name": "safe-browsing-enhanced-protection-android",
@@ -4517,7 +4517,7 @@
   },
   {
     "name": "select-to-speak-navigation-control",
-    "owners": [ "joelriley" ],
+    "owners": [ "joelriley@google.com" ],
     "expiry_milestone": 89
   },
   {
@@ -4786,11 +4786,6 @@
     "expiry_milestone": 95
   },
   {
-    "name": "system-tray-mic-gain",
-    "owners": [ "amehfooz", "tengs" ],
-    "expiry_milestone": 86
-  },
-  {
     "name": "tab-groups",
     "owners": [ "chrome-desktop-ui-sea@google.com", "connily" ],
     "expiry_milestone": 88
@@ -4938,13 +4933,13 @@
   },
   {
     "name": "turn-off-streaming-media-caching-always",
-    "owners": [ "shawnpi@microsoft.com", "cassew@google.com" ],
-    "expiry_milestone": 89
+    "owners": [ "wicarr@microsoft.com", "cassew@google.com" ],
+    "expiry_milestone": 91
   },
   {
     "name": "turn-off-streaming-media-caching-on-battery",
-    "owners": [ "shawnpi@microsoft.com", "cassew@google.com" ],
-    "expiry_milestone": 89
+    "owners": [ "wicarr@microsoft.com", "cassew@google.com" ],
+    "expiry_milestone": 91
   },
   {
     "name": "ui-debug-tools",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 79c6031a..046a069 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -984,6 +984,12 @@
 const char kEnableLoginDetectionDescription[] =
     "Allow user sign-in to be detected based on heuristics.";
 
+const char kEnablePciguardUiName[] =
+    "Enable Pciguard (Thunderbolt + USB4 tunneling) UI for settings";
+const char kEnablePciguardUiDescription[] =
+    "Enable toggling Pciguard settings through the Settings App. By default, "
+    "this flag is disabled.";
+
 const char kEnablePixelCanvasRecordingName[] = "Enable pixel canvas recording";
 const char kEnablePixelCanvasRecordingDescription[] =
     "Pixel canvas recording allows the compositor to raster contents aligned "
@@ -2283,11 +2289,6 @@
     "Enables viewing the current stylus battery level in the stylus tools "
     "menu.";
 
-const char kSystemTrayMicGainName[] = "Modify mic gain in the system tray";
-const char kSystemTrayMicGainDescription[] =
-    "Enables mic gain settings in the system tray audio "
-    "settings.";
-
 const char kTabEngagementReportingName[] = "Tab Engagement Metrics";
 const char kTabEngagementReportingDescription[] =
     "Tracks tab engagement and lifetime metrics.";
@@ -2620,6 +2621,11 @@
     "restrictions make it unlikely that a URL can be generated and actually "
     "work when shared.";
 
+const char kPreemtiveLinkToTextGenerationName[] =
+    "Preemptive generation of link to text";
+const char kPreemtiveLinkToTextGenerationDescription[] =
+    "Enables link to text to be generated in advance.";
+
 // Android ---------------------------------------------------------------------
 
 #if defined(OS_ANDROID)
@@ -3818,6 +3824,12 @@
     "Allow Android to use high-memory dalvik profile when applicable for "
     "high-memory devices.";
 
+const char kArcEnableUsapName[] =
+    "Enable ARC Unspecialized Application Processes";
+const char kArcEnableUsapDesc[] =
+    "Enable ARC Unspecialized Application Processes when applicable for "
+    "high-memory devices.";
+
 const char kArcUsbHostName[] = "Enable ARC USB host integration";
 const char kArcUsbHostDescription[] =
     "Allow Android apps to use USB host feature on ChromeOS devices.";
@@ -4053,11 +4065,6 @@
 const char kMovablePartialScreenshotDescription[] =
     "Allow partial screenshot region to be moved/resized via mouse/touch.";
 
-const char kEnableAdvancedPpdAttributesName[] =
-    "Enable advanced PPD attributes";
-const char kEnableAdvancedPpdAttributesDescription[] =
-    "Enable advanced settings on CUPS printers";
-
 const char kEnableAppDataSearchName[] = "Enable app data search in launcher";
 const char kEnableAppDataSearchDescription[] =
     "Allow launcher search to access data available through Firebase App "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f75a4bb..72f71ee 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -564,6 +564,9 @@
 extern const char kEnableNewDownloadBackendName[];
 extern const char kEnableNewDownloadBackendDescription[];
 
+extern const char kEnablePciguardUiName[];
+extern const char kEnablePciguardUiDescription[];
+
 extern const char kEnablePortalsName[];
 extern const char kEnablePortalsDescription[];
 
@@ -1324,9 +1327,6 @@
 extern const char kSyncSandboxName[];
 extern const char kSyncSandboxDescription[];
 
-extern const char kSystemTrayMicGainName[];
-extern const char kSystemTrayMicGainDescription[];
-
 extern const char kTabEngagementReportingName[];
 extern const char kTabEngagementReportingDescription[];
 
@@ -1522,6 +1522,9 @@
 extern const char kSharedHighlightingUseBlocklistName[];
 extern const char kSharedHighlightingUseBlocklistDescription[];
 
+extern const char kPreemtiveLinkToTextGenerationName[];
+extern const char kPreemtiveLinkToTextGenerationDescription[];
+
 // Android --------------------------------------------------------------------
 
 #if defined(OS_ANDROID)
@@ -2206,6 +2209,9 @@
 extern const char kArcUseHighMemoryDalvikProfileName[];
 extern const char kArcUseHighMemoryDalvikProfileDesc[];
 
+extern const char kArcEnableUsapName[];
+extern const char kArcEnableUsapDesc[];
+
 extern const char kArcUsbHostName[];
 extern const char kArcUsbHostDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 473184ed..905332f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -101,6 +101,7 @@
     &features::kMetricsSettingsAndroid,
     &features::kNetworkServiceInProcess,
     &features::kPredictivePrefetchingAllowedOnAllConnectionTypes,
+    &features::kPreemtiveLinkToTextGeneration,
     &features::kPrivacyReorderedAndroid,
     &features::kPrivacySandboxSettings,
     &features::kPrioritizeBootstrapTasks,
@@ -609,7 +610,7 @@
     "TabEngagementReportingAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kTabGroupsAndroid{"TabGroupsAndroid",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTabGroupsContinuationAndroid{
     "TabGroupsContinuationAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -618,7 +619,7 @@
     "TabGroupsUiImprovementsAndroid", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTabGridLayoutAndroid{"TabGridLayoutAndroid",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTabReparenting{"TabReparenting",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index c0803b76..b880bef 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -382,6 +382,7 @@
     public static final String PHOTO_PICKER_VIDEO_SUPPORT = "PhotoPickerVideoSupport";
     public static final String PORTALS = "Portals";
     public static final String PORTALS_CROSS_ORIGIN = "PortalsCrossOrigin";
+    public static final String PREEMTIVE_LINK_TO_TEXT_GENERATION = "PreemtiveLinkToTextGeneration";
     public static final String PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES =
             "PredictivePrefetchingAllowedOnAllConnectionTypes";
     public static final String PREFETCH_NOTIFICATION_SCHEDULING_INTEGRATION =
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index df41350..78529f1 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -8,6 +8,7 @@
 #include <Carbon/Carbon.h>
 
 #include "base/check.h"
+#include "base/feature_list.h"
 #include "base/mac/foundation_util.h"
 #include "base/no_destructor.h"
 #include "base/stl_util.h"
@@ -19,6 +20,7 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/platform_accelerator_cocoa.h"
 #import "ui/base/cocoa/nsmenuitem_additions.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_code_conversion_mac.h"
 
@@ -174,6 +176,14 @@
   if (base::FeatureList::IsEnabled(features::kTabSearch)) {
     keys->push_back({true,  true,  false, false, kVK_ANSI_A, IDC_TAB_SEARCH});
   }
+  if (base::FeatureList::IsEnabled(features::kUIDebugTools)) {
+    keys->push_back({false, true, true, true, kVK_ANSI_T,
+                     IDC_DEBUG_TOGGLE_TABLET_MODE});
+    keys->push_back({false, true, true, true, kVK_ANSI_V,
+                     IDC_DEBUG_PRINT_VIEW_TREE});
+    keys->push_back({false, true, true, true, kVK_ANSI_M,
+                     IDC_DEBUG_PRINT_VIEW_TREE_DETAILS});
+  }
   // clang-format on
   return *keys;
 }
diff --git a/chrome/browser/media/cast_mirroring_performance_browsertest.cc b/chrome/browser/media/cast_mirroring_performance_browsertest.cc
index ab0c19e3c..d440e0a 100644
--- a/chrome/browser/media/cast_mirroring_performance_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_performance_browsertest.cc
@@ -895,6 +895,8 @@
 
   void DidStart() override {}
   void DidStop() override {}
+  void LogInfoMessage(const std::string& message) override {}
+  void LogErrorMessage(const std::string& message) override {}
 
   // CastMessageChannel implementation
   void Send(mirroring::mojom::CastMessagePtr message) override {
diff --git a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
index 81bd592..2b80b06 100644
--- a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
@@ -214,6 +214,8 @@
   MOCK_METHOD1(OnError, void(mojom::SessionError));
   MOCK_METHOD0(DidStart, void());
   MOCK_METHOD0(DidStop, void());
+  MOCK_METHOD1(LogInfoMessage, void(const std::string&));
+  MOCK_METHOD1(LogErrorMessage, void(const std::string&));
 
   // mojom::CastMessageChannel mocks.
   MOCK_METHOD1(Send, void(mojom::CastMessagePtr));
diff --git a/chrome/browser/media/router/providers/cast/mirroring_activity.cc b/chrome/browser/media/router/providers/cast/mirroring_activity.cc
index b3d048d..d80de671 100644
--- a/chrome/browser/media/router/providers/cast/mirroring_activity.cc
+++ b/chrome/browser/media/router/providers/cast/mirroring_activity.cc
@@ -208,6 +208,13 @@
 }
 
 void MirroringActivity::OnError(SessionError error) {
+  logger_->LogError(
+      media_router::mojom::LogCategory::kMirroring, kLoggerComponent,
+      base::StringPrintf(
+          "Mirroring will stop. MirroringService.SessionError: %d",
+          static_cast<int>(error)),
+      route_.media_sink_id(), route_.media_source().id(),
+      route_.presentation_id());
   if (will_start_mirroring_timestamp_) {
     // An error was encountered while attempting to start mirroring.
     base::UmaHistogramEnumeration(kHistogramStartFailureNative, error);
@@ -236,6 +243,18 @@
   StopMirroring();
 }
 
+void MirroringActivity::LogInfoMessage(const std::string& message) {
+  logger_->LogInfo(media_router::mojom::LogCategory::kMirroring,
+                   kLoggerComponent, message, route_.media_sink_id(),
+                   route_.media_source().id(), route_.presentation_id());
+}
+
+void MirroringActivity::LogErrorMessage(const std::string& message) {
+  logger_->LogError(media_router::mojom::LogCategory::kMirroring,
+                    kLoggerComponent, message, route_.media_sink_id(),
+                    route_.media_source().id(), route_.presentation_id());
+}
+
 void MirroringActivity::Send(mirroring::mojom::CastMessagePtr message) {
   DCHECK(message);
   DVLOG(2) << "Relaying message to receiver: " << message->json_format_data;
diff --git a/chrome/browser/media/router/providers/cast/mirroring_activity.h b/chrome/browser/media/router/providers/cast/mirroring_activity.h
index ea87119..2ca6b41 100644
--- a/chrome/browser/media/router/providers/cast/mirroring_activity.h
+++ b/chrome/browser/media/router/providers/cast/mirroring_activity.h
@@ -57,6 +57,8 @@
   void OnError(mirroring::mojom::SessionError error) override;
   void DidStart() override;
   void DidStop() override;
+  void LogInfoMessage(const std::string& message) override;
+  void LogErrorMessage(const std::string& message) override;
 
   // CastMessageChannel implementation
   void Send(mirroring::mojom::CastMessagePtr message) override;
diff --git a/chrome/browser/metrics/browser_window_histogram_helper.cc b/chrome/browser/metrics/browser_window_histogram_helper.cc
deleted file mode 100644
index 268ff6d..0000000
--- a/chrome/browser/metrics/browser_window_histogram_helper.cc
+++ /dev/null
@@ -1,54 +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/browser/metrics/browser_window_histogram_helper.h"
-
-#include "components/startup_metric_utils/browser/startup_metric_utils.h"
-
-BrowserWindowHistogramHelper::~BrowserWindowHistogramHelper() {}
-
-// static
-std::unique_ptr<BrowserWindowHistogramHelper>
-BrowserWindowHistogramHelper::MaybeRecordValueAndCreateInstanceOnBrowserPaint(
-    ui::Compositor* compositor) {
-  static bool did_first_paint = false;
-  if (did_first_paint)
-    return nullptr;
-
-  did_first_paint = true;
-
-  return std::unique_ptr<BrowserWindowHistogramHelper>(
-      new BrowserWindowHistogramHelper(compositor));
-}
-
-BrowserWindowHistogramHelper::BrowserWindowHistogramHelper(
-    ui::Compositor* compositor) {
-  startup_metric_utils::RecordBrowserWindowFirstPaint(base::TimeTicks::Now());
-
-#if defined(OS_MAC)
-  if (!compositor) {
-    // In Cocoa version of Chromium, UI is rendered inside the main process
-    // using CoreAnimation compositor, and at this point everything is already
-    // visible to the user.
-    startup_metric_utils::RecordBrowserWindowFirstPaintCompositingEnded(
-        base::TimeTicks::Now());
-    return;
-  }
-#endif  // OS_MAC
-
-  scoped_observer_.Add(compositor);
-}
-
-void BrowserWindowHistogramHelper::OnCompositingEnded(
-    ui::Compositor* compositor) {
-  startup_metric_utils::RecordBrowserWindowFirstPaintCompositingEnded(
-      base::TimeTicks::Now());
-
-  scoped_observer_.RemoveAll();
-}
-
-void BrowserWindowHistogramHelper::OnCompositingShuttingDown(
-    ui::Compositor* compositor) {
-  scoped_observer_.RemoveAll();
-}
diff --git a/chrome/browser/metrics/browser_window_histogram_helper.h b/chrome/browser/metrics/browser_window_histogram_helper.h
deleted file mode 100644
index 648f3823..0000000
--- a/chrome/browser/metrics/browser_window_histogram_helper.h
+++ /dev/null
@@ -1,46 +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_BROWSER_METRICS_BROWSER_WINDOW_HISTOGRAM_HELPER_H_
-#define CHROME_BROWSER_METRICS_BROWSER_WINDOW_HISTOGRAM_HELPER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "base/time/time.h"
-#include "ui/compositor/compositor.h"
-#include "ui/compositor/compositor_observer.h"
-
-// Class that encapsulates logic of recording
-// Startup.BrowserWindow.FirstPaint* histograms.
-//
-// There's a dependency on ui/compositor therefore it can't be moved to
-// components/startup_metrics_utils.
-class BrowserWindowHistogramHelper : public ui::CompositorObserver {
- public:
-  ~BrowserWindowHistogramHelper() override;
-
-  // Call this when the Browser finishes painting its UI, and the user will see
-  // it after next Compositor frame swap.
-  // |compositor| is the compositor that composites the just-painted Browser
-  // widget, or nullptr in Cocoa (we're using CoreAnimation's compositor there).
-  // Returned object should stay alive until next |OnCompositingStarted|
-  // callback.
-  static std::unique_ptr<BrowserWindowHistogramHelper>
-  MaybeRecordValueAndCreateInstanceOnBrowserPaint(ui::Compositor* compositor);
-
- private:
-  explicit BrowserWindowHistogramHelper(ui::Compositor* compositor);
-
-  // ui::CompositorObserver:
-  void OnCompositingEnded(ui::Compositor* compositor) override;
-  void OnCompositingShuttingDown(ui::Compositor* compositor) override;
-
-  ScopedObserver<ui::Compositor, ui::CompositorObserver> scoped_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserWindowHistogramHelper);
-};
-
-#endif  // CHROME_BROWSER_METRICS_BROWSER_WINDOW_HISTOGRAM_HELPER_H_
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index 46769834..d789da77 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -558,7 +558,6 @@
 #endif
   );
 
-#if defined(OS_WIN)
   // Records whether or not PartitionAlloc-Everywhere is enabled, and whether
   // PCScan is enabled on top of it. This is meant for a 3-way experiment with 2
   // binaries:
@@ -581,11 +580,8 @@
       "Disabled"
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   );
-#endif  // defined(OS_WIN)
 
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-
-#if defined(OS_WIN)
   // Records whether or not BackupRefPtr and/or PCScan is enabled. This is meant
   // for a 3-way experiment with 2 binaries:
   // - binary A: deployed to 66% users, with half of them having PCScan on and
@@ -607,16 +603,6 @@
           : "Disabled"
 #endif
   );
-#else  // defined(OS_WIN)
-  ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("BackupRefPtr",
-#if BUILDFLAG(USE_BACKUP_REF_PTR)
-                                                            "Enabled"
-#else
-                                                            "Disabled"
-#endif
-  );
-#endif  // defined(OS_WIN)
-
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
   ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
diff --git a/chrome/browser/metrics/desktop_platform_features_metrics_provider.cc b/chrome/browser/metrics/desktop_platform_features_metrics_provider.cc
index 85751fe..b79b6d61a 100644
--- a/chrome/browser/metrics/desktop_platform_features_metrics_provider.cc
+++ b/chrome/browser/metrics/desktop_platform_features_metrics_provider.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/read_later/reading_list_model_factory.h"
 #include "components/reading_list/core/reading_list_model.h"
 #include "components/reading_list/features/reading_list_switches.h"
@@ -27,6 +29,14 @@
   kMaxValue = kDark,
 };
 
+bool AnyBrowserWindowHasName() {
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (!browser->user_title().empty())
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 DesktopPlatformFeaturesMetricsProvider::
@@ -59,4 +69,6 @@
       }
     }
   }
+
+  UMA_HISTOGRAM_BOOLEAN("Browser.AnyWindowHasName", AnyBrowserWindowHasName());
 }
diff --git a/chrome/browser/metrics/startup_metrics_browsertest.cc b/chrome/browser/metrics/startup_metrics_browsertest.cc
index 4edaf52..d8767df 100644
--- a/chrome/browser/metrics/startup_metrics_browsertest.cc
+++ b/chrome/browser/metrics/startup_metrics_browsertest.cc
@@ -21,7 +21,6 @@
 constexpr const char* kStartupMetrics[] = {
     "Startup.BrowserMessageLoopStartTime",
     "Startup.BrowserWindow.FirstPaint",
-    "Startup.BrowserWindow.FirstPaint.CompositingEnded",
     "Startup.BrowserWindowDisplay",
     "Startup.FirstWebContents.MainNavigationFinished",
     "Startup.FirstWebContents.MainNavigationStart",
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_observer.h b/chrome/browser/metrics/tab_stats/tab_stats_observer.h
index d246d85..fa25abb 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_observer.h
+++ b/chrome/browser/metrics/tab_stats/tab_stats_observer.h
@@ -29,6 +29,11 @@
   virtual void OnTabReplaced(content::WebContents* old_contents,
                              content::WebContents* new_contents) {}
 
+  // Called whenever a main frame navigation is committed in any of the observed
+  // tabs.
+  virtual void OnMainFrameNavigationCommitted(
+      content::WebContents* web_contents) {}
+
   // Records that there's been a direct user interaction with a tab, see the
   // comment for |DidGetUserInteraction| in
   // content/public/browser/web_contents_observer.h for a list of the possible
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
index 0d0a00d..df970bc 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
@@ -285,6 +285,12 @@
     navigation_time_ = navigation_handle->NavigationStart();
     ukm_source_id_ = ukm::ConvertToSourceId(
         navigation_handle->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
+
+    // Update observers.
+    for (TabStatsObserver& tab_stats_observer :
+         tab_stats_tracker_->tab_stats_observers_) {
+      tab_stats_observer.OnMainFrameNavigationCommitted(web_contents());
+    }
   }
 
   void DidGetUserInteraction(const blink::WebInputEvent& event) override {
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
index 6f3be7f..7424f092 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,6 +32,22 @@
   return base::StrCat({histogram_name, suffix});
 }
 
+class TestTabStatsObserver : public TabStatsObserver {
+ public:
+  // Functions used to update the counts.
+  void OnMainFrameNavigationCommitted(
+      content::WebContents* web_contents) override {
+    ++main_frame_committed_navigations_count_;
+  }
+
+  size_t main_frame_committed_navigations_count() {
+    return main_frame_committed_navigations_count_;
+  }
+
+ private:
+  size_t main_frame_committed_navigations_count_ = 0;
+};
+
 class TestTabStatsTracker : public TabStatsTracker {
  public:
   using TabStatsTracker::OnHeartbeatEvent;
@@ -191,6 +208,27 @@
 
 }  // namespace
 
+TEST_F(TabStatsTrackerTest, MainFrameCommittedNavigationTriggersUpdate) {
+  constexpr const char kFirstUrl[] = "https://parent.com/";
+
+  TestTabStatsObserver tab_stats_observer;
+  tab_stats_tracker_->AddObserverAndSetInitialState(&tab_stats_observer);
+  // Number of navigations starts of at zero.
+  ASSERT_EQ(tab_stats_observer.main_frame_committed_navigations_count(), 0u);
+
+  // Insert a new tab.
+  std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
+  tab_stats_tracker_->OnInitialOrInsertedTab(web_contents.get());
+
+  // Commit a main frame navigation on the observed tab.
+  auto* parent = content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents.get(), GURL(kFirstUrl));
+  DCHECK(parent);
+
+  // Navigation registered.
+  ASSERT_EQ(tab_stats_observer.main_frame_committed_navigations_count(), 1u);
+}
+
 TEST_F(TabStatsTrackerTest, OnResume) {
   // Makes sure that there's no sample initially.
   histogram_tester_.ExpectTotalCount(
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
index 3763f969..bee2eb3 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
@@ -137,4 +137,10 @@
   }
 }
 
+void TabUsageScenarioTracker::OnMainFrameNavigationCommitted(
+    content::WebContents* web_contents) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  usage_scenario_data_store_->OnTopLevelNavigation();
+}
+
 }  // namespace metrics
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.h b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.h
index 1d7e0fe..5bcadbf 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.h
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.h
@@ -39,6 +39,8 @@
   void OnTabInteraction(content::WebContents* web_contents) override;
   void OnMediaEffectivelyFullscreenChanged(content::WebContents* web_contents,
                                            bool is_fullscreen) override;
+  void OnMainFrameNavigationCommitted(
+      content::WebContents* web_contents) override;
 
   // display::DisplayObserver:
   void OnDisplayAdded(const display::Display& new_display) override;
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index cf9f5f3..f4803b8 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/heavy_ad_intervention/heavy_ad_service_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/browser/resource_tracker.h"
 #include "components/page_load_metrics/common/page_end_reason.h"
@@ -70,25 +71,6 @@
 const base::Feature kRestrictedNavigationAdTagging{
     "RestrictedNavigationAdTagging", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables or disables per-frame memory monitoring.
-const base::Feature kV8PerAdFrameMemoryMonitoring{
-    "V8PerAdFrameMemoryMonitoring", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Minimum time between memory measurements.
-const base::FeatureParam<int> kMemoryPollInterval = {
-    &kV8PerAdFrameMemoryMonitoring, "MemoryPollInterval", 40};
-
-// Available memory measurement modes.
-const base::FeatureParam<MeasurementMode>::Option memory_poll_modes[] = {
-    {MeasurementMode::kLazy, "lazy"},
-    {MeasurementMode::kBounded, "bounded"},
-    {MeasurementMode::kEagerForTesting, "eager_for_testing"}};
-
-// Memory measurement mode.
-const base::FeatureParam<MeasurementMode> kMemoryPollMode = {
-    &kV8PerAdFrameMemoryMonitoring, "MemoryPollMode", MeasurementMode::kLazy,
-    &memory_poll_modes};
-
 }  // namespace features
 
 namespace {
@@ -259,10 +241,7 @@
           std::make_unique<HeavyAdThresholdNoiseProvider>(
               heavy_ad_privacy_mitigations_enabled_ /* use_noise */)) {}
 
-AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() {
-  if (memory_request_)
-    memory_request_->RemoveObserver(this);
-}
+AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default;
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 AdsPageLoadMetricsObserver::OnStart(
@@ -429,18 +408,6 @@
       previous_data->UpdateForNavigation(ad_host, frame_navigated);
       return;
     }
-    if (base::FeatureList::IsEnabled(features::kV8PerAdFrameMemoryMonitoring) &&
-        !memory_request_) {
-      // The first ad subframe has been detected, so instantiate the
-      // memory request and add AdsPLMO as an observer. Without any ads, there
-      // would be no reason to monitor ad-frame memory usage and
-      // |memory_request_| wouldn't be needed.
-      memory_request_ = std::make_unique<
-          performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>(
-          base::TimeDelta::FromSeconds(features::kMemoryPollInterval.Get()),
-          features::kMemoryPollMode.Get());
-      memory_request_->AddObserver(this);
-    }
 
     // Construct a new FrameTreeData to track this ad frame, and update it for
     // the navigation.
@@ -716,37 +683,27 @@
   ad_frames_data_.erase(id_and_data);
 }
 
-void AdsPageLoadMetricsObserver::OnV8MemoryMeasurementAvailable(
-    performance_manager::RenderProcessHostId render_process_host_id,
-    const performance_manager::v8_memory::V8DetailedMemoryProcessData&
-        process_data,
-    const V8DetailedMemoryObserverAnySeq::FrameDataMap& frame_data) {
-  num_memory_updates_++;
+void AdsPageLoadMetricsObserver::OnV8MemoryChanged(
+    const std::vector<page_load_metrics::MemoryUpdate>& memory_updates) {
+  for (const auto& update : memory_updates) {
+    memory_update_count_++;
 
-  // Iterate through frames with available measurements.
-  for (const auto& map_pair : frame_data) {
-    content::GlobalFrameRoutingId frame_routing_id = map_pair.first;
-    content::RenderFrameHost* rfh =
-        content::RenderFrameHost::FromID(frame_routing_id);
+    content::RenderFrameHost* render_frame_host =
+        content::RenderFrameHost::FromID(update.routing_id);
 
-    if (!rfh) {
-      num_missed_memory_measurements_++;
+    if (!render_frame_host)
       continue;
-    }
 
-    uint64_t bytes_used = map_pair.second.v8_bytes_used();
-
-    FrameTreeNodeId frame_node_id = rfh->GetFrameTreeNodeId();
+    FrameTreeNodeId frame_node_id = render_frame_host->GetFrameTreeNodeId();
     FrameTreeData* ad_frame_data = FindFrameData(frame_node_id);
 
     if (ad_frame_data) {
-      int64_t delta = UpdateMemoryUsageForFrame(frame_node_id, bytes_used);
-      ad_frame_data->UpdateMemoryUsage(delta);
-      UpdateAggregateMemoryUsage(delta, ad_frame_data->visibility());
-    } else if (!rfh->GetParent()) {
-      // |rfh| is the main frame.
-      int64_t delta = UpdateMemoryUsageForFrame(frame_node_id, bytes_used);
-      aggregate_frame_data_->update_main_frame_memory(delta);
+      ad_frame_data->UpdateMemoryUsage(update.delta_bytes);
+      UpdateAggregateMemoryUsage(update.delta_bytes,
+                                 ad_frame_data->visibility());
+    } else if (!render_frame_host->GetParent()) {
+      // |render_frame_host| is the main frame.
+      aggregate_frame_data_->update_main_frame_memory(update.delta_bytes);
     }
   }
 }
@@ -803,33 +760,6 @@
   return is_new_ad ? resource->received_data_length - resource->delta_bytes : 0;
 }
 
-int64_t AdsPageLoadMetricsObserver::UpdateMemoryUsageForFrame(
-    FrameTreeNodeId frame_node_id,
-    uint64_t current_bytes) {
-  auto it = v8_current_memory_usage_map_.find(frame_node_id);
-
-  if (it == v8_current_memory_usage_map_.end()) {
-    v8_current_memory_usage_map_[frame_node_id] = current_bytes;
-    return current_bytes;
-  }
-
-  int64_t delta = current_bytes - it->second;
-  it->second = current_bytes;
-  return delta;
-}
-
-int64_t AdsPageLoadMetricsObserver::RemoveMemoryUsageForFrame(
-    FrameTreeNodeId frame_node_id) {
-  auto it = v8_current_memory_usage_map_.find(frame_node_id);
-
-  if (it == v8_current_memory_usage_map_.end())
-    return 0;
-
-  int64_t delta = -it->second;
-  v8_current_memory_usage_map_.erase(it);
-  return delta;
-}
-
 void AdsPageLoadMetricsObserver::ProcessResourceForPage(
     int process_id,
     const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
@@ -1050,8 +980,7 @@
                 visibility, visibility_data.bytes);
   ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
                 visibility, visibility_data.network_bytes);
-
-  if (memory_request_) {
+  if (base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring)) {
     ADS_HISTOGRAM("Memory.Aggregate.Max", PAGE_BYTES_HISTOGRAM, visibility,
                   visibility_data.memory.max_bytes_used());
   }
@@ -1071,14 +1000,11 @@
                 main_frame_resource_data.ad_network_bytes());
   ADS_HISTOGRAM("Bytes.MainFrame.Ads.Total2", PAGE_BYTES_HISTOGRAM, visibility,
                 main_frame_resource_data.ad_bytes());
-  if (memory_request_) {
+  if (base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring)) {
     PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Memory.MainFrame.Max",
                          aggregate_frame_data_->main_frame_max_memory());
     UMA_HISTOGRAM_COUNTS_10000("PageLoad.Clients.Ads.Memory.UpdateCount",
-                               num_memory_updates_);
-    UMA_HISTOGRAM_COUNTS_1000(
-        "PageLoad.Clients.Ads.Memory.MissedMeasurementCount",
-        num_missed_memory_measurements_);
+                               memory_update_count_);
   }
 }
 
@@ -1166,7 +1092,7 @@
                   visibility, resource_data.bytes());
     ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Network", PAGE_BYTES_HISTOGRAM,
                   visibility, resource_data.network_bytes());
-    if (memory_request_) {
+    if (base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring)) {
       ADS_HISTOGRAM("Memory.PerFrame.Max", PAGE_BYTES_HISTOGRAM, visibility,
                     ad_frame_data.v8_max_memory_bytes_used());
     }
@@ -1465,10 +1391,6 @@
   if (!frame_data)
     return;
 
-  int64_t delta_bytes = RemoveMemoryUsageForFrame(id);
-  frame_data->UpdateMemoryUsage(delta_bytes);
-  UpdateAggregateMemoryUsage(delta_bytes, frame_data->visibility());
-
   if (record_metrics)
     RecordPerFrameMetrics(*frame_data, GetDelegate().GetPageUkmSourceId());
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 2a10fc1..01970ce 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -21,22 +21,14 @@
 #include "components/blocklist/opt_out_blocklist/opt_out_blocklist_data.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "components/page_load_metrics/common/page_load_metrics.mojom-forward.h"
-#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/core/common/load_policy.h"
 #include "net/http/http_response_info.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 
-using MeasurementMode =
-    performance_manager::v8_memory::V8DetailedMemoryRequest::MeasurementMode;
-
 namespace features {
 extern const base::Feature kRestrictedNavigationAdTagging;
-extern const base::Feature kV8PerAdFrameMemoryMonitoring;
-extern const base::FeatureParam<int> kMemoryPollInterval;
-extern const base::FeatureParam<MeasurementMode>::Option memory_poll_modes[];
-extern const base::FeatureParam<MeasurementMode> kMemoryPollMode;
 }
 
 class HeavyAdBlocklist;
@@ -45,7 +37,6 @@
 // relevant per-frame and whole-page byte statistics.
 class AdsPageLoadMetricsObserver
     : public page_load_metrics::PageLoadMetricsObserver,
-      public performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq,
       public subresource_filter::SubresourceFilterObserver {
  public:
   using AggregateFrameData = ad_metrics::AggregateFrameData;
@@ -136,12 +127,8 @@
     heavy_ad_threshold_noise_provider_ = std::move(noise_provider);
   }
 
-  // performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq
-  void OnV8MemoryMeasurementAvailable(
-      performance_manager::RenderProcessHostId render_process_host_id,
-      const performance_manager::v8_memory::V8DetailedMemoryProcessData&
-          process_data,
-      const V8DetailedMemoryObserverAnySeq::FrameDataMap& frame_data) override;
+  void OnV8MemoryChanged(const std::vector<page_load_metrics::MemoryUpdate>&
+                             memory_updates) override;
 
   void UpdateAggregateMemoryUsage(int64_t bytes,
                                   ad_metrics::FrameVisibility visibility);
@@ -209,14 +196,6 @@
       int process_id,
       const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) const;
 
-  // Looks up the |frame_node_id| in the current memory usage map, updates it
-  // with the current bytes (or removes it), and returns the
-  // difference between the new value (0 if removed) and the old one, or the
-  // delta in the amount of memory the frame is using.
-  int64_t UpdateMemoryUsageForFrame(FrameTreeNodeId frame_node_id,
-                                    uint64_t current_bytes);
-  int64_t RemoveMemoryUsageForFrame(FrameTreeNodeId frame_node_id);
-
   // Updates page level counters for resource loads.
   void ProcessResourceForPage(
       int process_id,
@@ -340,17 +319,8 @@
   // The maximum ad density measurements for the page during its lifecycle.
   PageAdDensityTracker page_ad_density_tracker_;
 
-  // Tracks per ad-frame V8 memory measurements for the page during its
-  // lifecycle. Lazily initialized when the first ad is detected.
-  std::unique_ptr<performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>
-      memory_request_;
-
   // Tracks number of memory updates received.
-  int num_memory_updates_ = 0;
-
-  // Tracks number of per-frame memory measurements missed due to receipt
-  // after the corresponding RenderFrameHost has been destroyed.
-  int num_missed_memory_measurements_ = 0;
+  int memory_update_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserver);
 };
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
index 805a89b4..3dbd4d84 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -23,7 +23,9 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
+#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -199,7 +201,7 @@
   void SetUp() override {
     std::vector<base::Feature> enabled = {
         subresource_filter::kAdTagging, features::kSitePerProcess,
-        features::kV8PerAdFrameMemoryMonitoring};
+        features::kV8PerFrameMemoryMonitoring};
     std::vector<base::Feature> disabled = {};
 
     scoped_feature_list_.InitWithFeatures(enabled, disabled);
@@ -2249,7 +2251,7 @@
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled = {
         {subresource_filter::kAdTagging, {{}}},
         {features::kSitePerProcess, {{}}},
-        {features::kV8PerAdFrameMemoryMonitoring, memory_poll_params}};
+        {features::kV8PerFrameMemoryMonitoring, memory_poll_params}};
     std::vector<base::Feature> disabled = {};
 
     scoped_feature_list_.InitWithFeaturesAndParameters(enabled, disabled);
@@ -2282,12 +2284,14 @@
                        SingleAdFrame_MaxMemoryBytesRecorded) {
   base::HistogramTester histogram_tester;
 
-  // Instantiate a memory request and waiter to wait for a minimum
-  // number of memory measurements to be received.
+  // Instantiate a memory request and waiter to wait for expected
+  // memory measurements to be received.
   std::unique_ptr<performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>
       memory_request = std::make_unique<
           performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>(
-          base::TimeDelta::FromSeconds(1), MeasurementMode::kEagerForTesting);
+          base::TimeDelta::FromSeconds(1),
+          performance_manager::v8_memory::V8DetailedMemoryRequest::
+              MeasurementMode::kEagerForTesting);
   auto waiter = std::make_unique<MemoryMeasurementWaiter>();
   memory_request->AddObserver(waiter.get());
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index bd5bc70a..8cda3d2 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -31,6 +31,7 @@
 #include "components/blocklist/opt_out_blocklist/opt_out_store.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/page_load_metrics_util.h"
@@ -71,15 +72,8 @@
 
 namespace {
 
-using FrameDataMap =
-    AdsPageLoadMetricsObserver::V8DetailedMemoryObserverAnySeq::FrameDataMap;
 using FrameTreeNodeId = int;
 
-struct MemoryFrameData {
-  int frame_id;
-  uint64_t bytes_used;
-};
-
 struct ExpectedFrameBytes {
   ExpectedFrameBytes(size_t cached_kb, size_t uncached_kb)
       : cached_kb(cached_kb), uncached_kb(uncached_kb) {}
@@ -137,21 +131,6 @@
     AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider::
         kMaxNetworkThresholdNoiseBytes;
 
-FrameDataMap MakeFrameDataMap(int process_id,
-                              std::vector<MemoryFrameData> data) {
-  FrameDataMap data_map;
-
-  for (const auto& entry : data) {
-    content::GlobalFrameRoutingId global_id(process_id, entry.frame_id);
-    performance_manager::v8_memory::V8DetailedMemoryExecutionContextData
-        frame_data;
-    frame_data.set_v8_bytes_used(entry.bytes_used);
-    data_map[global_id] = frame_data;
-  }
-
-  return data_map;
-}
-
 // Calls PopulateRequiredTimingFields with |first_eligible_to_paint| and
 // |first_contentful_paint| fields temporarily nullified.
 void PopulateRequiredTimingFieldsExceptFEtPAndFCP(
@@ -819,20 +798,9 @@
                              "Activated.PostActivation");
   }
 
-  void OnV8MemoryMeasurementAvailable(
-      content::RenderProcessHost* render_process_host,
-      const std::vector<MemoryFrameData>& memory_data) {
-    int process_id = render_process_host->GetID();
-    performance_manager::RenderProcessHostId pm_process_id =
-        static_cast<performance_manager::RenderProcessHostId>(process_id);
-
-    FrameDataMap frame_data = MakeFrameDataMap(process_id, memory_data);
-    performance_manager::v8_memory::V8DetailedMemoryProcessData process_data;
-
-    if (ads_observer_) {
-      ads_observer_->OnV8MemoryMeasurementAvailable(pm_process_id, process_data,
-                                                    frame_data);
-    }
+  void SimulateV8MemoryChange(content::RenderFrameHost* render_frame_host,
+                              int64_t delta_bytes) {
+    tester()->SimulateMemoryUpdate(render_frame_host, delta_bytes);
   }
 
  private:
@@ -2962,7 +2930,7 @@
  public:
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(
-        features::kV8PerAdFrameMemoryMonitoring);
+        features::kV8PerFrameMemoryMonitoring);
     AdsPageLoadMetricsObserverTest::SetUp();
   }
 
@@ -2973,170 +2941,126 @@
 TEST_F(AdsMemoryMeasurementTest, SingleAdFrame_MaxMemoryBytesRecorded) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  content::RenderProcessHost* process = ad_frame->GetProcess();
 
   // Load kilobytes in frame so that aggregates are recorded.
   ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10);
 
-  // Set initial memory usage data.
-  std::vector<MemoryFrameData> memory_data = {
-      {ad_frame->GetRoutingID(), 10 * 1024 /* bytes_used */}};
-
   // Notify that memory measurement is available.
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  SimulateV8MemoryChange(ad_frame, 10 * 1024);
 
-  // Update memory usage. The max will change, as
-  // 40 > 10.
-  memory_data[0].bytes_used = 40 * 1024;
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  // Update memory usage. The max will change, as 30 is positive.
+  SimulateV8MemoryChange(ad_frame, 30 * 1024);
 
-  // Update memory usage. The max will remain the same, as
-  // 20 < 40.
-  memory_data[0].bytes_used = 20 * 1024;
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  // Update memory usage. The max will remain the same, as -20 is negative.
+  SimulateV8MemoryChange(ad_frame, -20 * 1024);
 
   // Navigate main frame to record histograms.
   NavigateMainFrame(kNonAdUrl);
 
-  histogram_tester().ExpectUniqueSample(kMemoryPerFrameMaxHistogramId, 40, 1);
-  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId, 40, 1);
+  histogram_tester().ExpectUniqueSample(kMemoryPerFrameMaxHistogramId, 10 + 30,
+                                        1);
+  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId, 10 + 30,
+                                        1);
   histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 3, 1);
 }
 
-TEST_F(AdsMemoryMeasurementTest,
-       MultiAdFramesSingleProcess_MaxMemoryBytesRecorded) {
+TEST_F(AdsMemoryMeasurementTest, MultiAdFramesNested_MaxMemoryBytesRecorded) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* ad_frame1 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  content::RenderProcessHost* process1 = ad_frame1->GetProcess();
 
   // Create a nested subframe with the same origin as its parent.
   RenderFrameHost* ad_frame2 = CreateAndNavigateSubFrame(kAdUrl, ad_frame1);
-  content::RenderProcessHost* process2 = ad_frame2->GetProcess();
-
-  // Expect a parent and child with the same origin on the same page
-  // to be hosted by the same process.
-  EXPECT_EQ(process1->GetID(), process2->GetID());
 
   // Load kilobytes in each frame so that aggregates are recorded.
   ResourceDataUpdate(ad_frame1, ResourceCached::kNotCached, 10);
   ResourceDataUpdate(ad_frame2, ResourceCached::kNotCached, 10);
 
-  // Set initial memory usage data.
-  std::vector<MemoryFrameData> memory_data = {
-      {ad_frame1->GetRoutingID(), 10 * 1024 /* bytes_used */},
-      {ad_frame2->GetRoutingID(), 10 * 1024 /* bytes_used */}};
-
   // Notify that memory measurement is available.
-  OnV8MemoryMeasurementAvailable(process1, memory_data);
+  SimulateV8MemoryChange(ad_frame1, 10 * 1024);
+  SimulateV8MemoryChange(ad_frame2, 10 * 1024);
 
   // Update memory usage. The max will change, as these values are both
-  // greater than the initial values.
-  memory_data[0].bytes_used = 40 * 1024;
-  memory_data[1].bytes_used = 20 * 1024;
-  OnV8MemoryMeasurementAvailable(process1, memory_data);
+  // positive.
+  SimulateV8MemoryChange(ad_frame1, 30 * 1024);
+  SimulateV8MemoryChange(ad_frame2, 10 * 1024);
 
   // Update memory usage. The max will remain the same, as these values
-  // are both less than the previous values.
-  memory_data[0].bytes_used = 5 * 1024;
-  memory_data[1].bytes_used = 15 * 1024;
-  OnV8MemoryMeasurementAvailable(process1, memory_data);
+  // are both negative.
+  SimulateV8MemoryChange(ad_frame1, -25 * 1024);
+  SimulateV8MemoryChange(ad_frame2, -5 * 1024);
 
   // Navigate main frame to record histograms.
   NavigateMainFrame(kNonAdUrl);
 
-  histogram_tester().ExpectUniqueSample(kMemoryPerFrameMaxHistogramId, 40 + 20,
-                                        1);
-  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId, 40 + 20,
-                                        1);
-  histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 3, 1);
+  histogram_tester().ExpectUniqueSample(kMemoryPerFrameMaxHistogramId,
+                                        10 + 10 + 30 + 10, 1);
+  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId,
+                                        10 + 10 + 30 + 10, 1);
+  histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 6, 1);
 }
 
 TEST_F(AdsMemoryMeasurementTest,
-       MultiAdFramesMultiProcess_MaxMemoryBytesRecorded) {
+       MultiAdFramesNonNested_MaxMemoryBytesRecorded) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* ad_frame1 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  content::RenderProcessHost* process1 = ad_frame1->GetProcess();
 
   // Create another ad subframe with a different origin.
   RenderFrameHost* ad_frame2 =
       CreateAndNavigateSubFrame(kOtherAdUrl, main_frame);
-  content::RenderProcessHost* process2 = ad_frame2->GetProcess();
-
-  // Only continue the test if the frames have different processes.
-  // Older versions of Android do not have site isolation.
-  if (process1->GetID() == process2->GetID())
-    return;
 
   // Load kilobytes in each frame so that aggregates are recorded.
   ResourceDataUpdate(ad_frame1, ResourceCached::kNotCached, 10);
   ResourceDataUpdate(ad_frame2, ResourceCached::kNotCached, 10);
 
-  // Set initial memory usage data.
-  std::vector<MemoryFrameData> memory_data1 = {
-      {ad_frame1->GetRoutingID(), 10 * 1024 /* bytes_used */}};
-  std::vector<MemoryFrameData> memory_data2 = {
-      {ad_frame2->GetRoutingID(), 10 * 1024 /* bytes_used */}};
-
   // Notify that memory measurement is available.
-  OnV8MemoryMeasurementAvailable(process1, memory_data1);
-  OnV8MemoryMeasurementAvailable(process2, memory_data2);
+  SimulateV8MemoryChange(ad_frame1, 10 * 1024);
+  SimulateV8MemoryChange(ad_frame2, 10 * 1024);
 
   // Update memory usage. The second max and aggregate max
   // will change.
-  memory_data1[0].bytes_used = 1 * 1024;
-  memory_data2[0].bytes_used = 100 * 1024;
-  OnV8MemoryMeasurementAvailable(process1, memory_data1);
-  OnV8MemoryMeasurementAvailable(process2, memory_data2);
+  SimulateV8MemoryChange(ad_frame1, -9 * 1024);
+  SimulateV8MemoryChange(ad_frame2, 100 * 1024);
 
   // Update memory usage. The aggregate max will change
   // again after the first update.
-  memory_data1[0].bytes_used = 2 * 1024;
-  memory_data2[0].bytes_used = 20 * 1024;
-  OnV8MemoryMeasurementAvailable(process1, memory_data1);
-  OnV8MemoryMeasurementAvailable(process2, memory_data2);
+  SimulateV8MemoryChange(ad_frame1, 1 * 1024);
+  SimulateV8MemoryChange(ad_frame2, -90 * 1024);
 
   // Update memory usage. The first max will change.
-  memory_data1[0].bytes_used = 50 * 1024;
-  memory_data2[0].bytes_used = 5 * 1024;
-  OnV8MemoryMeasurementAvailable(process1, memory_data1);
-  OnV8MemoryMeasurementAvailable(process2, memory_data2);
+  SimulateV8MemoryChange(ad_frame1, 50 * 1024);
+  SimulateV8MemoryChange(ad_frame2, -5 * 1024);
 
   // Navigate main frame to record histograms.
   NavigateMainFrame(kNonAdUrl);
 
-  histogram_tester().ExpectBucketCount(kMemoryPerFrameMaxHistogramId, 50, 1);
-  histogram_tester().ExpectBucketCount(kMemoryPerFrameMaxHistogramId, 100, 1);
-  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId, 2 + 100,
-                                        1);
+  histogram_tester().ExpectBucketCount(kMemoryPerFrameMaxHistogramId,
+                                       10 - 9 + 1 + 50, 1);
+  histogram_tester().ExpectBucketCount(kMemoryPerFrameMaxHistogramId, 10 + 100,
+                                       1);
+  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId,
+                                        10 - 9 + 10 + 100 + 1, 1);
   histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 8, 1);
 }
 
 TEST_F(AdsMemoryMeasurementTest, MainFrame_MaxMemoryBytesRecorded) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  content::RenderProcessHost* process = main_frame->GetProcess();
 
-  // Load kilobytes in each frame. |ad_frame| must be used for the test to
-  // compile.
+  // Load kilobytes in each frame. |ad_frame| must exist for ad metrics to be
+  // tracked.
   ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 1000);
   ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10);
 
-  // Set initial memory usage data.
-  std::vector<MemoryFrameData> memory_data = {
-      {main_frame->GetRoutingID(), 1000 * 1024 /* bytes_used */}};
-
   // Notify that memory measurement is available.
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  SimulateV8MemoryChange(main_frame, 1000 * 1024);
 
-  // Update memory usage. The max will also change, as this value is greater
-  // than the initial value.
-  memory_data[0].bytes_used = 2000 * 1024;
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  // Update memory usage. The max will also change, as this value is
+  // positive.
+  SimulateV8MemoryChange(main_frame, 1000 * 1024);
 
-  // Update memory usage. The max will remain the same, as this value is less
-  // than the previous value.
-  memory_data[0].bytes_used = 20 * 1024;
-  OnV8MemoryMeasurementAvailable(process, memory_data);
+  // Update memory usage. The max will remain the same, as this value is
+  // negative.
+  SimulateV8MemoryChange(main_frame, -1980 * 1024);
 
   // Navigate to record histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -3145,41 +3069,3 @@
                                         1);
   histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 3, 1);
 }
-
-TEST_F(AdsMemoryMeasurementTest, AdFrameDeleted_MaxMemoryBytesRecorded) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* ad_frame1 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  content::RenderProcessHost* process1 = ad_frame1->GetProcess();
-
-  // Create a nested subframe with the same origin as its parent.
-  RenderFrameHost* ad_frame2 = CreateAndNavigateSubFrame(kAdUrl, ad_frame1);
-
-  // Load kilobytes in each frame so that aggregates are recorded.
-  ResourceDataUpdate(ad_frame1, ResourceCached::kNotCached, 100);
-  ResourceDataUpdate(ad_frame2, ResourceCached::kNotCached, 100);
-
-  // Set initial memory usage data.
-  std::vector<MemoryFrameData> memory_data1 = {
-      {ad_frame1->GetRoutingID(), 100 * 1024 /* bytes_used */},
-      {ad_frame2->GetRoutingID(), 100 * 1024 /* bytes_used */}};
-
-  // Notify that memory measurement is available.
-  OnV8MemoryMeasurementAvailable(process1, memory_data1);
-
-  // Delete |ad_frame2|. The corresponding per-frame memory data will be
-  // deleted, changing the current usage, but the max will remain the same.
-  content::RenderFrameHostTester::For(ad_frame2)->Detach();
-
-  // Update memory usage. The max will change, as this value is
-  // greater than the sum of the initial values.
-  std::vector<MemoryFrameData> memory_data2 = {
-      {ad_frame1->GetRoutingID(), 500 * 1024 /* bytes_used */}};
-  OnV8MemoryMeasurementAvailable(process1, memory_data2);
-
-  // Navigate main frame to record histograms.
-  NavigateMainFrame(kNonAdUrl);
-
-  histogram_tester().ExpectUniqueSample(kMemoryPerFrameMaxHistogramId, 500, 1);
-  histogram_tester().ExpectUniqueSample(kMemoryAggregateMaxHistogramId, 500, 1);
-  histogram_tester().ExpectUniqueSample(kMemoryUpdateCountHistogramId, 2, 1);
-}
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
index 4613d0c..0a633c6 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/common/trace_event_common.h"
+#include "build/build_config.h"
 #include "cc/metrics/ukm_smoothness_data.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -53,6 +54,11 @@
 #include "chrome/browser/offline_pages/offline_page_tab_helper.h"
 #endif
 
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#endif  // !defined(OS_ANDROID)
+
 namespace {
 
 const char kOfflinePreviewsMimeType[] = "multipart/related";
@@ -251,6 +257,19 @@
                                    ->GetSiteInstance()
                                    ->GetLastProcessAssignmentOutcome();
 
+  // Android has a different tab model, and will need its own implementation.
+#if !defined(OS_ANDROID)
+  auto* contents = navigation_handle->GetWebContents();
+  DCHECK(contents);
+  if (Browser* browser = chrome::FindBrowserWithWebContents(contents)) {
+    int tab_index = browser->tab_strip_model()->GetIndexOfWebContents(contents);
+    if (tab_index != TabStripModel::kNoTab &&
+        browser->tab_strip_model()->GetTabGroupForTab(tab_index).has_value()) {
+      memories_signals_.is_existing_part_of_tab_group = true;
+    }
+  }
+#endif  // !defined(OS_ANDROID)
+
   return CONTINUE_OBSERVING;
 }
 
@@ -798,7 +817,7 @@
     builder.SetNavigationEntryOffset(navigation_entry_offset_);
     builder.SetMainDocumentSequenceNumber(main_document_sequence_number_);
   }
-  builder.SetOmniboxUrlCopied(omnibox_url_copied_);
+
   builder.Record(ukm::UkmRecorder::Get());
 }
 
@@ -1111,6 +1130,11 @@
   if (timing)
     RecordAbortMetrics(*timing, page_end_time, &builder);
 
+  // Add Memories signals to UKM.
+  builder.SetOmniboxUrlCopied(memories_signals_.omnibox_url_copied);
+  builder.SetIsExistingPartOfTabGroup(
+      memories_signals_.is_existing_part_of_tab_group);
+
   builder.Record(ukm::UkmRecorder::Get());
 }
 
@@ -1223,7 +1247,7 @@
     page_load_metrics::PageLoadMetricsEvent event) {
   if (event == page_load_metrics::PageLoadMetricsEvent::
                    OMNIBOX_URL_COPIED_TO_CLIPBOARD) {
-    omnibox_url_copied_ = true;
+    memories_signals_.omnibox_url_copied = true;
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
index 24f797b..c03af51a 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
@@ -287,9 +287,17 @@
 
   base::ReadOnlySharedMemoryMapping ukm_smoothness_data_;
 
-  // True if the user has cut or copied the omnibox URL to the clipboard for
-  // this page load.
-  bool omnibox_url_copied_ = false;
+  // This data is collected during the page lifetime and is meant to be sent to
+  // both UKM and History in OnComplete(). The recording is delayed until then
+  // to ensure that History has a visit entry ready for this navigation.
+  struct MemoriesSignals {
+    // True if the user has cut or copied the omnibox URL to the clipboard for
+    // this page load.
+    bool omnibox_url_copied = false;
+
+    // True if the page was in a tab group when the navigation was committed.
+    bool is_existing_part_of_tab_group = false;
+  } memories_signals_;
 
   DISALLOW_COPY_AND_ASSIGN(UkmPageLoadMetricsObserver);
 };
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index bfa4193..49dee5d 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -39,12 +39,14 @@
 #include "chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/translate_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h"
 #include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
 #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
@@ -74,6 +76,9 @@
   bool IsNewTabPageUrl(const GURL& url) override;
   bool IsPrerender(content::WebContents* web_contents) override;
   bool IsExtensionUrl(const GURL& url) override;
+  page_load_metrics::PageLoadMetricsMemoryTracker*
+  GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override;
 
  protected:
   // page_load_metrics::PageLoadMetricsEmbedderBase:
@@ -195,6 +200,16 @@
 #endif
 }
 
+page_load_metrics::PageLoadMetricsMemoryTracker*
+PageLoadMetricsEmbedder::GetMemoryTrackerForBrowserContext(
+    content::BrowserContext* browser_context) {
+  if (!base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring))
+    return nullptr;
+
+  return page_load_metrics::PageLoadMetricsMemoryTrackerFactory::
+      GetForBrowserContext(browser_context);
+}
+
 }  // namespace
 
 void InitializePageLoadMetricsForWebContents(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.cc b/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.cc
new file mode 100644
index 0000000..821ef183
--- /dev/null
+++ b/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.cc
@@ -0,0 +1,47 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h"
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsMemoryTracker*
+PageLoadMetricsMemoryTrackerFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<page_load_metrics::PageLoadMetricsMemoryTracker*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+PageLoadMetricsMemoryTrackerFactory*
+PageLoadMetricsMemoryTrackerFactory::GetInstance() {
+  return base::Singleton<PageLoadMetricsMemoryTrackerFactory>::get();
+}
+
+PageLoadMetricsMemoryTrackerFactory::PageLoadMetricsMemoryTrackerFactory()
+    : BrowserContextKeyedServiceFactory(
+          "PageLoadMetricsMemoryTracker",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+bool PageLoadMetricsMemoryTrackerFactory::ServiceIsCreatedWithBrowserContext()
+    const {
+  return base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring);
+}
+
+KeyedService* PageLoadMetricsMemoryTrackerFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new page_load_metrics::PageLoadMetricsMemoryTracker();
+}
+
+content::BrowserContext*
+PageLoadMetricsMemoryTrackerFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return context;
+}
+
+}  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h b/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h
new file mode 100644
index 0000000..6c9f19d4
--- /dev/null
+++ b/chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_METRICS_MEMORY_TRACKER_FACTORY_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_METRICS_MEMORY_TRACKER_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace page_load_metrics {
+
+class PageLoadMetricsMemoryTracker;
+
+class PageLoadMetricsMemoryTrackerFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static PageLoadMetricsMemoryTracker* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static PageLoadMetricsMemoryTrackerFactory* GetInstance();
+
+  PageLoadMetricsMemoryTrackerFactory();
+
+ private:
+  // BrowserContextKeyedServiceFactory:
+  bool ServiceIsCreatedWithBrowserContext() const override;
+
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace page_load_metrics
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_METRICS_MEMORY_TRACKER_FACTORY_H_
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
index afe8f2d6..3670f19d 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
@@ -274,9 +274,6 @@
 
             if (!mTabbedPaintPreview.isAttached()) return;
 
-            // If the tab is hidden as a result of pausing the activity we shouldn't remove it.
-            if (hidingType == TabHidingType.ACTIVITY_HIDDEN) return;
-
             remove(StartupPaintPreviewMetrics.ExitCause.TAB_HIDDEN);
         }
 
diff --git a/chrome/browser/platform_util.h b/chrome/browser/platform_util.h
index a94781c..47c6edd 100644
--- a/chrome/browser/platform_util.h
+++ b/chrome/browser/platform_util.h
@@ -102,6 +102,9 @@
 // gesture, if enabled in System Preferences. This function returns true if
 // the feature is supported and enabled, and false otherwise.
 bool IsSwipeTrackingFromScrollEventsEnabled();
+
+// Returns the active window which accepts keyboard inputs.
+NSWindow* GetActiveWindow();
 #endif
 
 // Returns true if the given browser window is in locked fullscreen mode
diff --git a/chrome/browser/platform_util_mac.mm b/chrome/browser/platform_util_mac.mm
index b9cc149..2e154a4 100644
--- a/chrome/browser/platform_util_mac.mm
+++ b/chrome/browser/platform_util_mac.mm
@@ -138,4 +138,8 @@
       && [NSEvent performSelector:selector];
 }
 
+NSWindow* GetActiveWindow() {
+  return [NSApp keyWindow];
+}
+
 }  // namespace platform_util
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index e7388c83..21b844a 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
@@ -1970,21 +1971,29 @@
 // before the browser is started.
 class WebAppInstallForceListPolicyTest : public ExtensionPolicyTest {
  public:
-  WebAppInstallForceListPolicyTest() {}
-  ~WebAppInstallForceListPolicyTest() override {}
+  WebAppInstallForceListPolicyTest()
+      : test_page_("/banners/manifest_test_page.html") {}
+  ~WebAppInstallForceListPolicyTest() override = default;
+  WebAppInstallForceListPolicyTest(const WebAppInstallForceListPolicyTest&) =
+      delete;
+  WebAppInstallForceListPolicyTest& operator=(
+      const WebAppInstallForceListPolicyTest&) = delete;
 
   void SetUpInProcessBrowserTestFixture() override {
     ExtensionPolicyTest::SetUpInProcessBrowserTestFixture();
     ASSERT_TRUE(embedded_test_server()->Start());
 
-    policy_app_url_ =
-        embedded_test_server()->GetURL("/banners/manifest_test_page.html");
+    policy_app_url_ = embedded_test_server()->GetURL(test_page_);
     base::Value url(policy_app_url_.spec());
     base::Value launch_container("window");
 
     base::Value item(base::Value::Type::DICTIONARY);
     item.SetKey("url", std::move(url));
     item.SetKey("default_launch_container", std::move(launch_container));
+    if (fallback_app_name_.has_value()) {
+      base::Value fallback_app_name(fallback_app_name_.value());
+      item.SetKey("fallback_app_name", std::move(fallback_app_name));
+    }
 
     base::Value list(base::Value::Type::LIST);
     list.Append(std::move(item));
@@ -1995,10 +2004,9 @@
   }
 
  protected:
+  std::string test_page_;
   GURL policy_app_url_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebAppInstallForceListPolicyTest);
+  base::Optional<std::string> fallback_app_name_;
 };
 
 IN_PROC_BROWSER_TEST_F(WebAppInstallForceListPolicyTest, StartUpInstallation) {
@@ -2013,6 +2021,141 @@
   EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id));
 }
 
+class WebAppInstallForceListPolicyWithAppFallbackNameManifestTest
+    : public WebAppInstallForceListPolicyTest {
+ public:
+  WebAppInstallForceListPolicyWithAppFallbackNameManifestTest() {
+    test_page_ = "/banners/manifest_test_page.html";
+    fallback_app_name_ = "fallback app name";
+  }
+
+  ~WebAppInstallForceListPolicyWithAppFallbackNameManifestTest() override =
+      default;
+  WebAppInstallForceListPolicyWithAppFallbackNameManifestTest(
+      const WebAppInstallForceListPolicyWithAppFallbackNameManifestTest&) =
+      delete;
+  WebAppInstallForceListPolicyWithAppFallbackNameManifestTest& operator=(
+      const WebAppInstallForceListPolicyWithAppFallbackNameManifestTest&) =
+      delete;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppInstallForceListPolicyWithAppFallbackNameManifestTest,
+    StartUpInstallationPWAFallbackName) {
+  const web_app::AppRegistrar& registrar =
+      web_app::WebAppProviderBase::GetProviderBase(browser()->profile())
+          ->registrar();
+  web_app::WebAppInstallObserver install_observer(browser()->profile());
+  base::Optional<web_app::AppId> app_id =
+      registrar.FindAppWithUrlInScope(policy_app_url_);
+  if (!app_id)
+    app_id = install_observer.AwaitNextInstall();
+  EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id));
+
+  // We specifically don't expect the fallback name to be used for a PWA
+  // except for the placeholder app.
+  EXPECT_NE(fallback_app_name_, registrar.GetAppShortName(*app_id));
+}
+
+// SAA == Site as App (a non-PWA installed as an app)
+class WebAppInstallForceListPolicySAATest
+    : public WebAppInstallForceListPolicyTest {
+ public:
+  WebAppInstallForceListPolicySAATest() {
+    test_page_ = "/banners/no_manifest_test_page.html";
+  }
+
+  ~WebAppInstallForceListPolicySAATest() override = default;
+  WebAppInstallForceListPolicySAATest(
+      const WebAppInstallForceListPolicySAATest&) = delete;
+  WebAppInstallForceListPolicySAATest& operator=(
+      const WebAppInstallForceListPolicySAATest&) = delete;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppInstallForceListPolicySAATest,
+                       StartUpInstallationSAA) {
+  const web_app::AppRegistrar& registrar =
+      web_app::WebAppProviderBase::GetProviderBase(browser()->profile())
+          ->registrar();
+  web_app::WebAppInstallObserver install_observer(browser()->profile());
+  base::Optional<web_app::AppId> app_id =
+      registrar.FindAppWithUrlInScope(policy_app_url_);
+  if (!app_id)
+    app_id = install_observer.AwaitNextInstall();
+  EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id));
+  EXPECT_NE(fallback_app_name_, registrar.GetAppShortName(*app_id));
+}
+
+class WebAppInstallForceListPolicyWithAppFallbackNameSAATest
+    : public WebAppInstallForceListPolicyTest {
+ public:
+  WebAppInstallForceListPolicyWithAppFallbackNameSAATest() {
+    test_page_ = "/banners/no_manifest_test_page.html";
+    fallback_app_name_ = "fallback app name";
+  }
+
+  ~WebAppInstallForceListPolicyWithAppFallbackNameSAATest() override = default;
+  WebAppInstallForceListPolicyWithAppFallbackNameSAATest(
+      const WebAppInstallForceListPolicyWithAppFallbackNameSAATest&) = delete;
+  WebAppInstallForceListPolicyWithAppFallbackNameSAATest& operator=(
+      const WebAppInstallForceListPolicyWithAppFallbackNameSAATest&) = delete;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppInstallForceListPolicyWithAppFallbackNameSAATest,
+                       StartUpInstallationSAAFallbackName) {
+  const web_app::AppRegistrar& registrar =
+      web_app::WebAppProviderBase::GetProviderBase(browser()->profile())
+          ->registrar();
+  web_app::WebAppInstallObserver install_observer(browser()->profile());
+  base::Optional<web_app::AppId> app_id =
+      registrar.FindAppWithUrlInScope(policy_app_url_);
+  if (!app_id)
+    app_id = install_observer.AwaitNextInstall();
+  EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id));
+  EXPECT_EQ(fallback_app_name_, registrar.GetAppShortName(*app_id));
+}
+
+class WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest
+    : public WebAppInstallForceListPolicyTest {
+ public:
+  WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest() {
+    test_page_ = "/close-socket";
+    fallback_app_name_ = "fallback app name";
+  }
+
+  ~WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest() override =
+      default;
+  WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest(
+      const WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest&) =
+      delete;
+  WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest& operator=(
+      const WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest&) =
+      delete;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppInstallForceListPolicyPlaceholderWithAppFallbackNameTest,
+    StartUpInstallationPlaceholderFallbackName) {
+  const web_app::AppRegistrar& registrar =
+      web_app::WebAppProviderBase::GetProviderBase(browser()->profile())
+          ->registrar();
+  web_app::WebAppInstallObserver install_observer(browser()->profile());
+  base::Optional<web_app::AppId> app_id =
+      registrar.FindAppWithUrlInScope(policy_app_url_);
+  if (!app_id)
+    app_id = install_observer.AwaitNextInstall();
+  EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id));
+  EXPECT_EQ(fallback_app_name_, registrar.GetAppShortName(*app_id));
+
+  std::unique_ptr<web_app::ExternallyInstalledWebAppPrefs>
+      externally_installed_app_prefs =
+          std::make_unique<web_app::ExternallyInstalledWebAppPrefs>(
+              browser()->profile()->GetPrefs());
+  ASSERT_TRUE(
+      externally_installed_app_prefs->LookupPlaceholderAppId(policy_app_url_)
+          .has_value());
+}
+
 // Fixture for tests that have two profiles with a different policy for each.
 class ExtensionPolicyTest2Contexts : public PolicyTest {
  public:
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 83a30c4..405a01f6 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -175,9 +175,9 @@
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/shell.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
 #include "chrome/browser/ui/ash/chrome_screenshot_grabber.h"
 #include "chrome/browser/ui/ash/chrome_screenshot_grabber_test_observer.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 0b69c0d..a47827b 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -277,6 +277,8 @@
 #include "chrome/browser/chromeos/extensions/printing/printing_api_handler.h"
 #endif
 #include "chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h"
+#include "chrome/browser/ash/system/automatic_reboot_manager.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/chromeos/borealis/borealis_prefs.h"
 #include "chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h"
 #include "chrome/browser/chromeos/file_system_provider/registry.h"
@@ -325,8 +327,6 @@
 #include "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/chromeos/settings/device_settings_cache.h"
-#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
 #include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 774117c1..9c582f49 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
 #include "chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.h"
 #include "chrome/browser/permissions/last_tab_standing_tracker_factory.h"
@@ -348,6 +349,7 @@
 #if !defined(OS_ANDROID)
   NTPResourceCacheFactory::GetInstance();
 #endif
+  page_load_metrics::PageLoadMetricsMemoryTrackerFactory::GetInstance();
   PasswordStoreFactory::GetInstance();
   PermissionAuditingServiceFactory::GetInstance();
   ProfileProtoDBFactory<
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 27b7e58..e752e01 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -260,7 +260,8 @@
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
 
-  web_contents_->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
+  auto* tester = content::WebContentsTester::For(web_contents_);
+  tester->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
   ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit);
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 0abb7ac..e571c77 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -474,6 +474,7 @@
     "$externs_path/chromeos_info_private.js",
     "$externs_path/chrome.js",
     "$externs_path/chrome_extensions.js",
+    "$externs_path/clipboard.js",
     "$externs_path/command_line_private.js",
     "$externs_path/login_state.js",
     "$externs_path/metrics_private.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 10334ff..3e5dd5d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -72,12 +72,12 @@
     /** @type {!LiveRegions} @private */
     this.liveRegions_ = new LiveRegions(this);
 
-    document.addEventListener('copy', this.onClipboardEvent_);
-    document.addEventListener('cut', this.onClipboardEvent_);
-    document.addEventListener('paste', this.onClipboardEvent_);
+    /** @private {string|undefined} */
+    this.lastClipboardEvent_;
 
-    /** @private {boolean} */
-    this.preventPasteOutput_ = false;
+    chrome.clipboard.onClipboardDataChanged.addListener(
+        this.onClipboardDataChanged_.bind(this));
+    document.addEventListener('copy', this.onClipboardCopyEvent_.bind(this));
 
     /**
      * Maps a non-desktop root automation node to a range position suitable for
@@ -356,36 +356,36 @@
   }
 
   /**
-   * Detects various clipboard events and provides spoken output.
-   *
-   * Note that paste is explicitly skipped sometimes because during a copy or
-   * cut, the copied or cut text is retrieved by pasting into a fake text
-   * area. To prevent this from triggering paste output, this staste is
-   * tracked via a field.
+   * Processes the copy clipboard event.
    * @param {!Event} evt
    * @private
    */
-  onClipboardEvent_(evt) {
-    let text = '';
-    if (evt.type === 'paste') {
-      if (this.preventPasteOutput_) {
-        this.preventPasteOutput_ = false;
-        return;
-      }
-      text = evt.clipboardData.getData('text');
-      ChromeVox.tts.speak(Msgs.getMsg(evt.type, [text]), QueueMode.QUEUE);
-    } else if (evt.type === 'copy' || evt.type === 'cut') {
-      this.preventPasteOutput_ = true;
-      const textarea = document.createElement('textarea');
-      document.body.appendChild(textarea);
-      textarea.focus();
-      document.execCommand('paste');
-      const clipboardContent = textarea.value;
-      textarea.remove();
-      ChromeVox.tts.speak(
-          Msgs.getMsg(evt.type, [clipboardContent]), QueueMode.FLUSH);
-      ChromeVoxState.instance.pageSel_ = null;
+  onClipboardCopyEvent_(evt) {
+    // This should always be 'copy', but is still important to set for the below
+    // extension event.
+    this.lastClipboardEvent_ = evt.type;
+  }
+
+  /** @private */
+  onClipboardDataChanged_() {
+    // A DOM-based clipboard event always comes before this Chrome extension
+    // clipboard event. We only care about 'copy' events, which gets set above.
+    if (!this.lastClipboardEvent_) {
+      return;
     }
+
+    const eventType = this.lastClipboardEvent_;
+    this.lastClipboardEvent_ = undefined;
+
+    const textarea = document.createElement('textarea');
+    document.body.appendChild(textarea);
+    textarea.focus();
+    document.execCommand('paste');
+    const clipboardContent = textarea.value;
+    textarea.remove();
+    ChromeVox.tts.speak(
+        Msgs.getMsg(eventType, [clipboardContent]), QueueMode.FLUSH);
+    ChromeVoxState.instance.pageSel_ = null;
   }
 
   /** @private */
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 846e1e34..3136e817 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -22,9 +22,9 @@
     this.forceContextualLastOutput();
   }
 
-  doGesture(gesture) {
+  doGesture(gesture, opt_x, opt_y) {
     return () => {
-      GestureCommandHandler.onAccessibilityGesture_(gesture);
+      GestureCommandHandler.onAccessibilityGesture_(gesture, opt_x, opt_y);
     };
   }
 
@@ -3290,3 +3290,45 @@
         .replay();
   });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'TouchEditingState', function() {
+  const mockFeedback = this.createMockFeedback();
+  const site = `
+    <p>Start</p>
+    <input type="text"></input>
+  `;
+  this.runWithLoadedTree(site, function(rootNode) {
+    const bounds = rootNode.find({role: RoleType.TEXT_FIELD}).location;
+    mockFeedback.expectSpeech('Start')
+        .call(doGesture(
+            chrome.accessibilityPrivate.Gesture.TOUCH_EXPLORE, bounds.left,
+            bounds.top))
+        .expectSpeech('Edit text', 'Double tap to start editing')
+        .call(doGesture(
+            chrome.accessibilityPrivate.Gesture.CLICK, bounds.left, bounds.top))
+        .expectSpeech('Edit text', 'is editing')
+        .replay();
+  });
+});
+
+TEST_F('ChromeVoxBackgroundTest', 'TouchGesturesProducesEarcons', function() {
+  const mockFeedback = this.createMockFeedback();
+  const site = `
+    <p>Start</p>
+    <button>ok</button>
+    <a href="chromevox.com">cancel</a>
+  `;
+  this.runWithLoadedTree(site, function(rootNode) {
+    mockFeedback.expectSpeech('Start')
+        .call(doGesture(chrome.accessibilityPrivate.Gesture.SWIPE_RIGHT1))
+        .expectSpeech('ok', 'Button')
+        .expectEarcon(Earcon.BUTTON)
+        .call(doGesture(chrome.accessibilityPrivate.Gesture.SWIPE_RIGHT1))
+        .expectSpeech('cancel', 'Link')
+        .expectEarcon(Earcon.LINK)
+        .call(doGesture(chrome.accessibilityPrivate.Gesture.SWIPE_LEFT1))
+        .expectSpeech('ok', 'Button')
+        .expectEarcon(Earcon.BUTTON)
+        .replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
index e8ae4e8..496d90b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
@@ -99,12 +99,8 @@
     }
 
     // Decide whether to announce and sync this event.
-    const isFocusOnRoot =
-        evt.type === 'focus' && evt.target === evt.target.root;
     if (!DesktopAutomationHandler.announceActions &&
-        evt.eventFrom === 'action' &&
-        (EventSourceState.get() !== EventSourceType.TOUCH_GESTURE ||
-         isFocusOnRoot)) {
+        evt.eventFrom === 'action') {
       return;
     }
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
index 0899010..853a065 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
+++ b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
@@ -16,47 +16,40 @@
   "icons.js",
 ]
 
+processed_json_files = [
+  "emoji_13_1_ordering.json",
+  "emoji_test_ordering.json",
+]
+
 resources_grd_file = "$target_gen_dir/resources.grd"
-components_grdp_file = "$target_gen_dir/components.grdp"
-data_grdp_file = "$target_gen_dir/data.grdp"
+preprocessed_grdp_file = "$target_gen_dir/preprocessed.grdp"
 
 generate_grd("build_grd") {
   deps = [
-    ":build_components_grdp",
-    ":build_data_grdp",
+    ":build_preprocessed_grdp",
+    ":emoji_data",
   ]
   grd_prefix = "emoji_picker"
   out_grd = resources_grd_file
   input_files = [
     "constants.js",
-    "emoji_test_ordering.json",
     "events.js",
     "index.html",
     "store.js",
     "types.js",
   ]
-  grdp_files = [
-    components_grdp_file,
-    data_grdp_file,
-  ]
+  grdp_files = [ preprocessed_grdp_file ]
   input_files_base_dir = rebase_path(".", "//")
 }
 
-generate_grd("build_components_grdp") {
+generate_grd("build_preprocessed_grdp") {
   deps = [ ":web_components" ]
   grd_prefix = "emoji_picker"
-  out_grd = components_grdp_file
-  input_files = component_js_files
+  out_grd = preprocessed_grdp_file
+  input_files = component_js_files + processed_json_files
   input_files_base_dir = rebase_path(target_gen_dir, root_build_dir)
 }
 
-generate_grd("build_data_grdp") {
-  grd_prefix = "emoji_picker"
-  out_grd = data_grdp_file
-  input_files = [ "emoji_13_1_ordering.json" ]
-  input_files_base_dir = rebase_path("//third_party/emoji-metadata/src", "//")
-}
-
 grit("resources") {
   # These arguments are needed since the grd is generated at build time.
   enable_input_discovery_for_gn_analyze = false
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
index d5ed32d..d350f0a 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
@@ -52,7 +52,7 @@
     on-contextmenu="onContextMenu"
     on-mouseup="onMouseUp"
     disabled="[[disabled]]">
-  [[_renderEmoji(emoji)]]
+  [[emoji]]
 </button>
 <template is="dom-if" if="[[variantsVisible]]">
   <emoji-variants variants="[[variants]]"></emoji-variants>
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
index 03c901a..b745ed3 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
@@ -7,7 +7,7 @@
 import {beforeNextRender, html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {createCustomEvent, EMOJI_BUTTON_CLICK, EMOJI_VARIANTS_SHOWN} from './events.js';
-import {Codepoints} from './types.js';
+import {Emoji} from './types.js';
 
 export class EmojiButton extends PolymerElement {
   static get is() {
@@ -20,9 +20,9 @@
 
   static get properties() {
     return {
-      /** @type {!Codepoints} */
-      emoji: {type: Array, readonly: true},
-      /** @type {!Array<Codepoints>} */
+      /** @type {!string} */
+      emoji: {type: String, readonly: true},
+      /** @type {Array<Emoji>} */
       variants: {type: Array, readonly: true},
       /** @type {!boolean} */
       variantsVisible: {type: Boolean, value: false},
@@ -38,8 +38,8 @@
   onClick(ev) {
     if (this.disabled)
       return;
-    this.dispatchEvent(createCustomEvent(
-        EMOJI_BUTTON_CLICK, {emoji: this._renderEmoji(this.emoji)}));
+    this.dispatchEvent(
+        createCustomEvent(EMOJI_BUTTON_CLICK, {emoji: this.emoji}));
   }
 
   onContextMenu(ev) {
@@ -72,13 +72,6 @@
   _className(variants) {
     return variants && variants.length > 0 ? 'has-variants' : '';
   }
-
-  /**
-   * @param {Codepoints} codepoints
-   */
-  _renderEmoji(codepoints) {
-    return String.fromCodePoint(...codepoints);
-  }
 }
 
 customElements.define(EmojiButton.is, EmojiButton);
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
index b2f8499d..1853ba7 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
@@ -19,7 +19,7 @@
 <div id="heading">[[data.group]]</div>
 <div id="emoji">
   <template is="dom-repeat" items="[[data.emoji]]">
-    <emoji-button emoji="[[item.base]]" variants="[[item.alternates]]">
+    <emoji-button emoji="[[item.base.string]]" variants="[[item.alternates]]">
     </emoji-button>
   </template>
 </div>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index dd6dc23..bc4e1432 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -14,7 +14,7 @@
 import {EmojiButton} from './emoji_button.js';
 import {createCustomEvent, EMOJI_BUTTON_CLICK, EMOJI_DATA_LOADED, EMOJI_VARIANTS_SHOWN, EmojiVariantsShownEvent, GROUP_BUTTON_CLICK} from './events.js';
 import {RecentEmojiStore} from './store.js';
-import {Codepoints, Emoji, EmojiData, EmojiGroup} from './types.js';
+import {Emoji, EmojiGroup, EmojiGroupData, EmojiVariants} from './types.js';
 
 const EMOJI_ORDERING_JSON = '/emoji_13_1_ordering.json';
 
@@ -35,11 +35,13 @@
  * Constructs the emoji group data structure from a given list of emoji
  * strings. Note: returned emoji have no variants.
  *
- * @param {!Array<Codepoints>} recentEmoji list of recently used emoji strings.
- * @return {!Array<!Emoji>} list of emoji data structures
+ * @param {!Array<string>} recentEmoji list of recently used emoji strings.
+ * @return {!Array<EmojiVariants>} list of emoji data structures
  */
 function makeRecentlyUsed(recentEmoji) {
-  return recentEmoji.map(emoji => ({base: emoji, alternates: []}));
+  return recentEmoji.map(
+      emoji =>
+          ({base: {string: emoji, name: '', keywords: []}, alternates: []}));
 }
 
 export class EmojiPicker extends PolymerElement {
@@ -56,7 +58,7 @@
       /** @type {!string} */
       emojiDataUrl: {type: String, value: EMOJI_ORDERING_JSON},
       emojiGroupTabs: {type: Array},
-      /** @private {?EmojiData} */
+      /** @private {?EmojiGroupData} */
       emojiData: {
         type: Object,
         observer: 'onEmojiDataChanged',
@@ -214,7 +216,7 @@
 
 
   onEmojiDataLoaded(data) {
-    this.emojiData = /** @type {!EmojiData} */ (JSON.parse(data));
+    this.emojiData = /** @type {!EmojiGroupData} */ (JSON.parse(data));
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
index 0cf216a8..a4d86b3 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
@@ -67,7 +67,7 @@
 -->
 
 <div id="grid-base-emoji">
-  <template is="dom-if" if="[[baseEmoji.length]]">
+  <template is="dom-if" if="[[baseEmoji]]">
     <emoji-button emoji="[[baseEmoji]]"></emoji-button>
   </template>
 </div>
@@ -82,21 +82,21 @@
       U+1F3FE EMOJI MODIFIER FITZPATRICK TYPE-5
       U+1F3FF EMOJI MODIFIER FITZPATRICK TYPE-6
     -->
-    <emoji-button emoji="[127995]" disabled></emoji-button>
-    <emoji-button emoji="[127996]" disabled></emoji-button>
-    <emoji-button emoji="[127997]" disabled></emoji-button>
-    <emoji-button emoji="[127998]" disabled></emoji-button>
-    <emoji-button emoji="[127999]" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FB;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FC;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FD;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FE;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FF;" disabled></emoji-button>
   </template>
 </div>
 
 <div id="grid-skin-tone-left" class="skin-tone">
   <template is="dom-if" if="[[showSkinTones]]">
-    <emoji-button emoji="[127995]" disabled></emoji-button>
-    <emoji-button emoji="[127996]" disabled></emoji-button>
-    <emoji-button emoji="[127997]" disabled></emoji-button>
-    <emoji-button emoji="[127998]" disabled></emoji-button>
-    <emoji-button emoji="[127999]" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FB;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FC;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FD;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FE;" disabled></emoji-button>
+    <emoji-button emoji="&#x1F3FF;" disabled></emoji-button>
   </template>
 </div>
 
@@ -104,7 +104,7 @@
   <template is="dom-repeat" items="[[variantRows]]" as="row">
     <div class="variant-row">
       <template is="dom-repeat" items="[[row]]" as="emoji">
-        <emoji-button emoji="[[emoji]]"></emoji-button>
+        <emoji-button emoji="[[emoji.string]]"></emoji-button>
       </template>
     </div>
   </template>
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
index 0911d6a..a8f259b0 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {Codepoints} from './types.js';
+import {Emoji} from './types.js';
 
 const GENDER_FEMALE = 9792;       // U+2640 FEMALE_SIGN
 const SKIN_TONE_MEDIUM = 127997;  // U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4
@@ -13,12 +13,13 @@
 /**
  * Determines if the given list of variants has any variant which contains
  * the given codepoint.
- * @param {!Array<Codepoints>} variants
+ * @param {!Array<!Emoji>} variants
  * @param {!number} codepoint
  * @return {boolean}
  */
 function hasVariation(variants, codepoint) {
-  return variants.findIndex(x => x.includes(codepoint)) !== -1;
+  const codepointString = String.fromCodePoint(codepoint);
+  return variants.findIndex(x => x.string.includes(codepointString)) !== -1;
 }
 
 
@@ -57,11 +58,11 @@
 
   static get properties() {
     return {
-      /** @type {!Array<Codepoints>} */
+      /** @type {!Array<Emoji>} */
       variants: {type: Array, readonly: true},
-      /** @private {!Array<!Array<Codepoints>>} */
+      /** @private {!Array<!Array<Emoji>>} */
       variantRows: {type: Array},
-      /** @private {?Codepoints} */
+      /** @private {?string} */
       baseEmoji: {type: Array},
       /** @private {boolean} */
       showSkinTones: {type: Boolean},
@@ -83,7 +84,7 @@
 
     if (isFamily || isTwoPeople) {
       // for these cases, the first variant is the generic one.
-      this.baseEmoji = this.variants[0];
+      this.baseEmoji = this.variants[0].string;
     } else {
       this.baseEmoji = null;
     }
diff --git a/chrome/browser/resources/chromeos/emoji_picker/store.js b/chrome/browser/resources/chromeos/emoji_picker/store.js
index 0f9aebb..6196031 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/store.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/store.js
@@ -2,15 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Codepoints} from './types.js';
-
 const LOCALSTORAGE_KEY = 'emoji-recently-used';
-const MAX_RECENTS = 14;
+const MAX_RECENTS = 18;
 
 /**
- * Recently used emoji, most recent first. Each emoji is stored as an array
- * of codepoints.
- * @typedef {!Array<Codepoints>}
+ * Recently used emoji, most recent first. Each emoji is stored as a string.
+ * @typedef {!Array<string>} RecentlyUsedEmoji
  */
 let RecentlyUsedEmoji;
 
@@ -22,7 +19,13 @@
   if (!stored) {
     return [];
   }
-  return /** @type {?} */ (JSON.parse(stored));
+  const parsed = /** @type {?} */ (JSON.parse(stored));
+  if (parsed[0] && Array.isArray(parsed[0])) {
+    // if stored data is in older codepoint format, ignore it.
+    return [];
+  }
+
+  return parsed;
 }
 
 /**
@@ -44,15 +47,12 @@
    */
   bumpEmoji(newEmoji) {
     // find and remove newEmoji from array if it previously existed.
-    const oldIndex =
-        this.data.findIndex(x => String.fromCodePoint(...x) === newEmoji);
+    const oldIndex = this.data.findIndex(x => x === newEmoji);
     if (oldIndex !== -1) {
       this.data.splice(oldIndex, 1);
     }
-    // insert newEmoji's codepoints to front of array.
-    // first, split newEmoji into an array of strings where each string is
-    // one codepoint. then, convert each codepoint to its numerical value.
-    this.data.unshift([...newEmoji].map(x => x.codePointAt(0)));
+    // insert newEmoji to the front of the array.
+    this.data.unshift(newEmoji);
     // slice from end of array if it exceeds MAX_RECENTS.
     if (this.data.length > MAX_RECENTS) {
       // setting length is sufficient to truncate an array.
diff --git a/chrome/browser/resources/chromeos/emoji_picker/types.js b/chrome/browser/resources/chromeos/emoji_picker/types.js
index 7b768da4..2ee89635 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/types.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/types.js
@@ -3,21 +3,21 @@
 // found in the LICENSE file.
 
 /**
- * @typedef {!Array<!number>}
- */
-export let Codepoints;
-
-/**
- * @typedef {{base: Codepoints, alternates: !Array<Codepoints>}}
+ * @typedef {{string: string, name: string, keywords: !Array<!string>}} Emoji
  */
 export let Emoji;
 
 /**
- * @typedef {{group: string, emoji: Array<Emoji>}}
+ * @typedef {{base: Emoji, alternates: !Array<Emoji>}} EmojiVariants
+ */
+export let EmojiVariants;
+
+/**
+ * @typedef {{group: string, emoji: !Array<EmojiVariants>}} EmojiGroup
  */
 export let EmojiGroup;
 
 /**
- * @typedef {Array<EmojiGroup>} EmojiData
+ * @typedef {Array<EmojiGroup>} EmojiGroupData
  */
-export let EmojiData;
+export let EmojiGroupData;
diff --git a/chrome/browser/resources/commander/app.js b/chrome/browser/resources/commander/app.js
index d305893..410119c 100644
--- a/chrome/browser/resources/commander/app.js
+++ b/chrome/browser/resources/commander/app.js
@@ -92,6 +92,8 @@
       this.browserProxy_.promptCancelled();
       this.promptText_ = null;
       this.$.input.value = this.savedInput_;
+      e.preventDefault();
+      this.onInput_();
     }
   }
 
@@ -119,6 +121,7 @@
       this.promptText_ = viewModel.promptText || null;
       this.savedInput_ = this.$.input.value;
       this.$.input.value = '';
+      this.onInput_();
     }
   }
 
diff --git a/chrome/browser/resources/supervised_user_internals/DIR_METADATA b/chrome/browser/resources/family_link_user_internals/DIR_METADATA
similarity index 100%
rename from chrome/browser/resources/supervised_user_internals/DIR_METADATA
rename to chrome/browser/resources/family_link_user_internals/DIR_METADATA
diff --git a/chrome/browser/resources/supervised_user_internals/OWNERS b/chrome/browser/resources/family_link_user_internals/OWNERS
similarity index 100%
rename from chrome/browser/resources/supervised_user_internals/OWNERS
rename to chrome/browser/resources/family_link_user_internals/OWNERS
diff --git a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.css b/chrome/browser/resources/family_link_user_internals/family_link_user_internals.css
similarity index 100%
rename from chrome/browser/resources/supervised_user_internals/supervised_user_internals.css
rename to chrome/browser/resources/family_link_user_internals/family_link_user_internals.css
diff --git a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html b/chrome/browser/resources/family_link_user_internals/family_link_user_internals.html
similarity index 80%
rename from chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
rename to chrome/browser/resources/family_link_user_internals/family_link_user_internals.html
index ab6ee64..c6e8a82 100644
--- a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
+++ b/chrome/browser/resources/family_link_user_internals/family_link_user_internals.html
@@ -7,13 +7,10 @@
 <html lang="en">
 <head>
 <meta charset="utf-8">
-<title>Supervised User Internals</title>
+<title>Family Link User Internals</title>
 <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
 <link rel="stylesheet" href="chrome://resources/css/list.css">
-<link rel="stylesheet" href="supervised_user_internals.css">
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/assert.js"></script>
-<script src="chrome://resources/js/util.js"></script>
+<link rel="stylesheet" href="family_link_user_internals.css">
 </head>
 
 <body>
@@ -23,7 +20,7 @@
     <table class="section-details">
       <tr jsselect="data"
             jsvalues="class:$this.is_valid ? '' : 'uninitialized'"
-            jseval='chrome.supervised_user_internals.highlightIfChanged(
+            jseval='highlightIfChanged(
                 this, this.children[1].innerText, stat_value)'>
         <td class="detail" jscontent="stat_name" width=50%></td>
         <td class="value" jscontent="stat_value" width=50%></td>
@@ -53,7 +50,7 @@
   </div>
 
   <div id='user-settings' class="section">
-    <h2>Supervised User Settings</h2>
+    <h2>Family Link User Settings</h2>
     <table class="section-details">
       <tr jsselect="settings">
         <td jscontent="key"></td>
@@ -74,7 +71,6 @@
   </div>
 </div>
 
-<script src="chrome://resources/js/jstemplate_compiled.js"></script>
-<script src="supervised_user_internals.js"></script>
+<script type="module" src="family_link_user_internals.js"></script>
 </body>
 </html>
diff --git a/chrome/browser/resources/family_link_user_internals/family_link_user_internals.js b/chrome/browser/resources/family_link_user_internals/family_link_user_internals.js
new file mode 100644
index 0000000..530c8f48
--- /dev/null
+++ b/chrome/browser/resources/family_link_user_internals/family_link_user_internals.js
@@ -0,0 +1,125 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/js/jstemplate_compiled.js';
+
+import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {$} from 'chrome://resources/js/util.m.js';
+
+function initialize() {
+  function submitURL(event) {
+    $('try-url-result').textContent = '';
+    $('manual-allowlist').textContent = '';
+    $('allowlists').textContent = '';
+    sendWithPromise('tryURL', $('try-url-input').value)
+        .then(({allowResult, manual, allowLists}) => {
+          $('try-url-result').textContent = allowResult;
+          $('manual-allowlist').textContent = manual;
+          $('allowlists').textContent = allowlists;
+        });
+    event.preventDefault();
+  }
+
+  $('try-url').addEventListener('submit', submitURL);
+
+  // Make the prototype jscontent element disappear.
+  jstProcess({}, $('filtering-results-container'));
+
+  addWebUIListener('basic-info-received', receiveBasicInfo);
+  addWebUIListener('user-settings-received', receiveUserSettings);
+  addWebUIListener('filtering-result-received', receiveFilteringResult);
+
+  chrome.send('registerForEvents');
+
+  chrome.send('getBasicInfo');
+}
+
+function highlightIfChanged(node, oldVal, newVal) {
+  function clearHighlight() {
+    node.removeAttribute('highlighted');
+  }
+
+  const oldStr = oldVal.toString();
+  const newStr = newVal.toString();
+  if (oldStr != '' && oldStr != newStr) {
+    // Note the addListener function does not end up creating duplicate
+    // listeners.  There can be only one listener per event at a time.
+    // See https://developer.mozilla.org/en/DOM/element.addEventListener
+    node.addEventListener('animationend', clearHighlight, false);
+    node.setAttribute('highlighted', '');
+  }
+}
+
+function receiveBasicInfo(info) {
+  jstProcess(new JsEvalContext(info), $('basic-info'));
+
+  // Hack: Schedule another refresh after a while.
+  setTimeout(function() {
+    chrome.send('getBasicInfo');
+  }, 5000);
+}
+
+function receiveUserSettings(settings) {
+  if (settings === null) {
+    $('user-settings').classList.add('hidden');
+    return;
+  }
+
+  $('user-settings').classList.remove('hidden');
+
+  // The user settings are returned as an object, flatten them into a
+  // list of key/value pairs for easier consumption by the HTML template.
+  // This is not done recursively, values are passed as their JSON
+  // representation.
+  const kvpairs = Object.keys(settings).map(function(key) {
+    return {key: key, value: JSON.stringify(settings[key], null, 2)};
+  });
+
+  jstProcess(new JsEvalContext({settings: kvpairs}), $('user-settings'));
+}
+
+/**
+ * Helper to determine if an element is scrolled to its bottom limit.
+ * @param {Element} elem element to check
+ * @return {boolean} true if the element is scrolled to the bottom
+ */
+function isScrolledToBottom(elem) {
+  return elem.scrollHeight - elem.scrollTop == elem.clientHeight;
+}
+
+/**
+ * Helper to scroll an element to its bottom limit.
+ * @param {Element} elem element to be scrolled
+ */
+function scrollToBottom(elem) {
+  elem.scrollTop = elem.scrollHeight - elem.clientHeight;
+}
+
+/** Container for accumulated filtering results. */
+const filteringResults = [];
+
+/**
+ * Callback for incoming filtering results.
+ * @param {Object} result The result.
+ */
+function receiveFilteringResult(result) {
+  filteringResults.push(result);
+
+  const container = $('filtering-results-container');
+
+  // Scroll to the bottom if we were already at the bottom.  Otherwise, leave
+  // the scrollbar alone.
+  const shouldScrollDown = isScrolledToBottom(container);
+
+  jstProcess(new JsEvalContext({results: filteringResults}), container);
+
+  if (shouldScrollDown) {
+    scrollToBottom(container);
+  }
+}
+
+// Export on window since it is called with jseval.
+window.highlightIfChanged = highlightIfChanged;
+
+document.addEventListener('DOMContentLoaded', initialize);
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index e730896..c9a2e099 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -189,6 +189,19 @@
 window.chrome.embeddedSearch.newTabPage.blacklistSearchSuggestionWithHash;
 
 /**
+ * @param {number} task_version
+ * @param {number} task_id
+ */
+window.chrome.embeddedSearch.newTabPage.blocklistSearchSuggestion;
+
+/**
+ * @param {number} task_version
+ * @param {number} task_id
+ * @param {string} hash
+ */
+window.chrome.embeddedSearch.newTabPage.blocklistSearchSuggestionWithHash;
+
+/**
  * No params.
  */
 window.chrome.embeddedSearch.newTabPage.confirmThemeChanges;
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 4d972e7..5d33377 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -505,12 +505,16 @@
     "chromeos/os_settings_icons_css.m.js",
     "chromeos/os_settings_page/main_page_behavior.m.js",
     "chromeos/os_settings_routes.m.js",
+    "chromeos/os_settings_search_box/os_search_result_row.m.js",
+    "chromeos/os_settings_search_box/os_settings_search_box.m.js",
+    "chromeos/os_toolbar/os_toolbar.m.js",
     "chromeos/parental_controls_page/parental_controls_browser_proxy.m.js",
     "chromeos/parental_controls_page/parental_controls_page.m.js",
     "chromeos/personalization_page/change_picture.m.js",
     "chromeos/personalization_page/change_picture_browser_proxy.m.js",
     "chromeos/personalization_page/personalization_page.m.js",
     "chromeos/personalization_page/wallpaper_browser_proxy.m.js",
+    "chromeos/personalization_page/wallpaper_constants.m.js",
     "chromeos/personalization_page/wallpaper_subpage.m.js",
     "chromeos/pref_to_setting_metric_converter.m.js",
     "chromeos/route_origin_behavior.m.js",
@@ -1008,6 +1012,8 @@
     "chromeos/personalization_page/personalization_page.js",
     "chromeos/personalization_page/wallpaper_browser_proxy.html",
     "chromeos/personalization_page/wallpaper_browser_proxy.js",
+    "chromeos/personalization_page/wallpaper_constants.html",
+    "chromeos/personalization_page/wallpaper_constants.js",
     "chromeos/personalization_page/wallpaper_subpage.js",
     "chromeos/personalization_page/wallpaper_subpage.html",
     "chromeos/pref_to_setting_metric_converter.html",
@@ -1240,10 +1246,10 @@
     #"os_settings_main:closure_compile_module",
     #"os_settings_menu:closure_compile_module",
     "os_settings_page:closure_compile_module",
+    "os_settings_search_box:closure_compile_module",
 
-    #"os_settings_search_box:closure_compile_module",
     #"os_settings_ui:closure_compile_module",
-    #"os_toolbar:closure_compile_module",
+    "os_toolbar:closure_compile_module",
     "parental_controls_page:closure_compile_module",
     "personalization_page:closure_compile_module",
   ]
@@ -1342,6 +1348,10 @@
 
 js_library("search_handler.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/search_handler.m.js" ]
+  deps = [
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile",
+    "//ui/webui/resources/js:cr.m",
+  ]
   extra_deps = [ ":modulize" ]
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 107d8cd..51e32e8 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -91,6 +91,8 @@
                                    "settings.printing.sortPrinters|sortPrinters",
                                    "settings.recordLockScreenProgress|recordLockScreenProgress",
                                    "settings.recordSettingChange|recordSettingChange",
+                                   "settings.setUserActionRecorderForTesting|setUserActionRecorderForTesting",
+                                   "settings.recordSearch|recordSearch",
                                    "settings.Router|Router",
                                    "settings.routes|routes",
                                    "settings.setSearchHandlerForTesting|setSearchHandlerForTesting",
@@ -146,7 +148,7 @@
                              "chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_browser_proxy.html|GoogleAssistantBrowserProxy,GoogleAssistantBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/guest_os/guest_os_browser_proxy.html|GuestOsBrowserProxy, GuestOsBrowserProxyImpl, GuestOsSharedUsbDevice, CROSTINI_TYPE, PLUGIN_VM_TYPE",
                              "chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts_browser_proxy.html|KerberosAccount,KerberosAccountsBrowserProxyImpl,KerberosAccountsBrowserProxy,KerberosErrorType,KerberosConfigErrorCode,ValidateKerberosConfigResult",
-                             "chrome/browser/resources/settings/chromeos/metrics_recorder.html|recordSettingChange",
+                             "chrome/browser/resources/settings/chromeos/metrics_recorder.html|recordSettingChange, recordSearch, setUserActionRecorderForTesting",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.html|MultiDeviceBrowserProxy,MultiDeviceBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.html|MultiDeviceSettingsMode,MultiDeviceFeature,MultiDeviceFeatureState,MultiDevicePageContentData,PhoneHubNotificationAccessStatus,SmartLockSignInEnabledState",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.html|MultiDeviceFeatureBehavior",
@@ -171,6 +173,7 @@
                              "chrome/browser/resources/settings/chromeos/os_settings_routes.html|OsSettingsRoutes",
                              "chrome/browser/resources/settings/chromeos/personalization_page/change_picture_browser_proxy.html|ChangePictureBrowserProxy,ChangePictureBrowserProxyImpl,DefaultImage",
                              "chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.html|WallpaperBrowserProxy,WallpaperBrowserProxyImpl",
+                             "chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.html|WallpaperCollection",
                              "chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.html|ParentalControlsBrowserProxy,ParentalControlsBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/route_origin_behavior.html|RouteOriginBehaviorImpl,RouteOriginBehavior",
                              "chrome/browser/resources/settings/appearance_page/fonts_browser_proxy.html|FontsBrowserProxy,FontsBrowserProxyImpl",
@@ -192,7 +195,7 @@
                              "chrome/browser/resources/settings/about_page/about_page_browser_proxy.html|AboutPageBrowserProxyImpl,AboutPageUpdateInfo,AboutPageBrowserProxy,browserChannelToI18nId,VersionInfo,ChannelInfo,BrowserChannel,isTargetChannelMoreStable,UpdateStatus,UpdateStatusChangedEvent,RegulatoryInfo,TPMFirmwareUpdateStatusChangedEvent",
                              "chrome/browser/resources/settings/chromeos/os_about_page/device_name_browser_proxy.html|DeviceNameBrowserProxy,DeviceNameBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.html|MainPageBehavior",
-                             "chrome/browser/resources/settings/chromeos/searh_handler.html|getSearchHandler, setSearchHandlerForTesting",
+                             "chrome/browser/resources/settings/chromeos/search_handler.html|getSearchHandler, setSearchHandlerForTesting",
                              "ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.html|NetworkListenerBehavior",
                              "ui/webui/resources/cr_elements/cr_slider/cr_slider.html|SliderTick",
                              "ui/webui/resources/html/assert.html|assert,assertNotReached",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 78c0274..5965c119 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -52,6 +52,7 @@
 import '../prefs/prefs.m.js';
 import './personalization_page/personalization_page.m.js';
 import './personalization_page/change_picture.m.js';
+import './personalization_page/wallpaper_subpage.m.js';
 import './os_a11y_page/tts_subpage.m.js';
 import './os_people_page/account_manager.m.js';
 import './parental_controls_page/parental_controls_page.m.js';
@@ -60,7 +61,10 @@
 import './os_about_page/channel_switcher_dialog.m.js';
 import './os_about_page/detailed_build_info.m.js';
 import './os_about_page/update_warning_dialog.m.js';
+import './os_toolbar/os_toolbar.m.js';
 import './os_search_page/os_search_page.m.js';
+import './os_settings_search_box/os_search_result_row.m.js';
+import './os_settings_search_box/os_settings_search_box.m.js';
 import './os_icons.m.js';
 import './os_settings_icons_css.m.js';
 import './os_apps_page/android_apps_subpage.m.js';
@@ -131,3 +135,4 @@
 export {ChangePictureBrowserProxy, ChangePictureBrowserProxyImpl} from './personalization_page/change_picture_browser_proxy.m.js';
 export {WallpaperBrowserProxyImpl} from './personalization_page/wallpaper_browser_proxy.m.js';
 export {getSearchHandler, setSearchHandlerForTesting} from './search_handler.m.js';
+export {setUserActionRecorderForTesting, recordPageFocus, recordPageBlur, recordClick, recordNavigation, recordSearch, recordSettingChange} from './metrics_recorder.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_settings_search_box/BUILD.gn
index cf181cb5..a089311 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("../os_settings.gni")
 
 js_type_check("closure_compile") {
   uses_legacy_modules = true
@@ -39,19 +40,26 @@
   externs_list = [ "$externs_path/metrics_private.js" ]
 }
 
-# TODO: Uncomment as the Polymer3 migration makes progress.
-#js_type_check("closure_compile_module") {
-#  is_polymer3 = true
-#  deps = [
-#    ":os_search_result_row.m",
-#    ":os_settings_search_box.m"
-#  ]
-#}
+js_type_check("closure_compile_module") {
+  is_polymer3 = true
+  deps = [
+    ":os_search_result_row.m",
+    ":os_settings_search_box.m",
+  ]
+}
 
 js_library("os_search_result_row.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    "..:os_icons.m",
+    "..:os_route.m",
+    "..:search_handler.m",
+    "../..:router.m",
+    "//third_party/polymer/v3_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js/cr/ui:focus_row_behavior.m",
   ]
   extra_deps = [ ":os_search_result_row_module" ]
   externs_list = [ "$externs_path/metrics_private.js" ]
@@ -60,7 +68,18 @@
 js_library("os_settings_search_box.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":os_search_result_row.m",
+    "..:metrics_recorder.m",
+    "..:os_route.m",
+    "..:search_handler.m",
+    "../..:router.m",
+    "//third_party/polymer/v3_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer",
+    "//third_party/polymer/v3_0/components-chromium/iron-dropdown:iron-dropdown",
+    "//third_party/polymer/v3_0/components-chromium/iron-list:iron-list",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar_search_field.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":os_settings_search_box_module" ]
   externs_list = [ "$externs_path/metrics_private.js" ]
@@ -79,10 +98,14 @@
   js_file = "os_search_result_row.js"
   html_file = "os_search_result_row.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
 
 polymer_modulizer("os_settings_search_box") {
   js_file = "os_settings_search_box.js"
   html_file = "os_settings_search_box.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
index 118d6c6..9c12745 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
@@ -4,7 +4,6 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_row.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/mojo/mojo/public/mojom/base/string16.mojom.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
@@ -13,6 +12,7 @@
 <link rel="import" href="../search_handler.html">
 <link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="../../router.html">
+<link rel="import" href="../os_route.html">
 
 <dom-module id="os-settings-search-box">
   <template>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.js b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.js
index a4b4bcb2..eea0a0ed 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.js
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-(function() {
-'use strict';
-
-const mojom = chromeos.settings.mojom;
-
 const MAX_NUM_SEARCH_RESULTS = 5;
 
 const SEARCH_REQUEST_METRIC_NAME = 'ChromeOS.Settings.SearchRequests';
@@ -82,7 +77,7 @@
      * <os-search-result-row>. This property is bound to the <iron-list>. Note
      * that when an item is selected, its associated <os-search-result-row>
      * is not focus()ed at the same time unless it is explicitly clicked/tapped.
-     * @private {!mojom.SearchResult}
+     * @private {!chromeos.settings.mojom.SearchResult}
      */
     selectedItem_: {
       type: Object,
@@ -92,7 +87,7 @@
      * Prevent user deselection by tracking last item selected. This item must
      * only be assigned to an item within |this.$.searchResultList|, and not
      * directly to |this.selectedItem_| or an item within |this.searchResults_|.
-     * @private {!mojom.SearchResult}
+     * @private {!chromeos.settings.mojom.SearchResult}
      */
     lastSelectedItem_: {
       type: Object,
@@ -100,7 +95,7 @@
 
     /**
      * Passed into <iron-list>. Exactly one result is the selectedItem_.
-     * @private {!Array<!mojom.SearchResult>}
+     * @private {!Array<!chromeos.settings.mojom.SearchResult>}
      */
     searchResults_: {
       type: Array,
@@ -215,7 +210,9 @@
    */
   getSelectedOsSearchResultRow_() {
     return assert(
-        this.$.searchResultList.querySelector('os-search-result-row[selected]'),
+        /** @type {!OsSearchResultRowElement} */ (
+            this.$.searchResultList.querySelector(
+                'os-search-result-row[selected]')),
         'No OsSearchResultRow is selected.');
   },
 
@@ -259,7 +256,7 @@
     settings.getSearchHandler()
         .search(
             queryMojoString16, MAX_NUM_SEARCH_RESULTS,
-            mojom.ParentResultBehavior.kAllowParentResults)
+            chromeos.settings.mojom.ParentResultBehavior.kAllowParentResults)
         .then(response => {
           const latencyMs = Date.now() - timeOfSearchRequest;
           chrome.metricsPrivate.recordTime(
@@ -280,7 +277,8 @@
   /**
    * Updates search results UI when settings search results are fetched.
    * @param {string} query The string used to find search results.
-   * @param {!Array<!mojom.SearchResult>} results Array of search results.
+   * @param {!Array<!chromeos.settings.mojom.SearchResult>} results Array of
+   * search results.
    * @private
    */
   onSearchResultsReceived_(query, results) {
@@ -378,7 +376,8 @@
   },
 
   /**
-   * @param {!mojom.SearchResult} item The search result item in searchResults_.
+   * @param {!chromeos.settings.mojom.SearchResult} item The search result item
+   * in searchResults_.
    * @return {boolean} True if the item is selected.
    * @private
    */
@@ -399,7 +398,8 @@
    * Returns the correct tab index since <iron-list>'s default tabIndex property
    * does not automatically add selectedItem_'s <os-search-result-row> to the
    * default navigation flow, unless the user explicitly clicks on the row.
-   * @param {!mojom.SearchResult} item The search result item in searchResults_.
+   * @param {!chromeos.settings.mojom.SearchResult} item The search result item
+   * in searchResults_.
    * @return {number} A 0 if the row should be in the navigation flow, or a -1
    *     if the row should not be in the navigation flow.
    * @private
@@ -512,4 +512,3 @@
     }
   },
 });
-})();
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn
index 519c798..4f6cd29 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("../os_settings.gni")
 
 js_type_check("closure_compile") {
   uses_legacy_modules = true
@@ -16,18 +17,19 @@
   ]
 }
 
-# TODO: Uncomment as the Polymer3 migration makes progress.
-#js_type_check("closure_compile_module") {
-#  is_polymer3 = true
-#  deps = [
-#    ":os_toolbar.m"
-#  ]
-#}
+js_type_check("closure_compile_module") {
+  is_polymer3 = true
+  deps = [ ":os_toolbar.m" ]
+}
 
 js_library("os_toolbar.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    "../os_settings_search_box:os_settings_search_box.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar_search_field.m",
+    "//ui/webui/resources/js:load_time_data.m",
   ]
   extra_deps = [ ":os_toolbar_module" ]
 }
@@ -42,4 +44,6 @@
   js_file = "os_toolbar.js"
   html_file = "os_toolbar.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
index 34c14c6..7088da69 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
@@ -6,6 +6,7 @@
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
 <link rel="import" href="../os_settings_search_box/os_settings_search_box.html">
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
index 685eef8..bd057c5 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
@@ -13,6 +13,7 @@
     ":change_picture",
     ":change_picture_browser_proxy",
     ":personalization_page",
+    ":wallpaper_constants",
     ":wallpaper_subpage",
   ]
 }
@@ -55,14 +56,22 @@
 }
 
 js_library("wallpaper_browser_proxy") {
-  deps = [ "//ui/webui/resources/js:cr" ]
+  deps = [
+    ":wallpaper_constants",
+    "//ui/webui/resources/js:cr",
+  ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
 
+js_library("wallpaper_constants") {
+}
+
 js_library("wallpaper_subpage") {
   deps = [
     ":wallpaper_browser_proxy",
+    ":wallpaper_constants",
     "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:i18n_behavior",
   ]
 }
 
@@ -73,6 +82,7 @@
     ":change_picture_browser_proxy.m",
     ":personalization_page.m",
     ":wallpaper_browser_proxy.m",
+    ":wallpaper_constants.m",
     ":wallpaper_subpage.m",
   ]
 }
@@ -124,15 +134,23 @@
 
 js_library("wallpaper_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.m.js" ]
+  deps = [ ":wallpaper_constants.m" ]
   externs_list = [ "$externs_path/chrome_send.js" ]
   extra_deps = [ ":modulize" ]
 }
 
+js_library("wallpaper_constants.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.m.js" ]
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("wallpaper_subpage.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.m.js" ]
   deps = [
     ":wallpaper_browser_proxy.m",
+    ":wallpaper_constants.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":wallpaper_subpage_module" ]
 }
@@ -180,6 +198,7 @@
   input_files = [
     "change_picture_browser_proxy.js",
     "wallpaper_browser_proxy.js",
+    "wallpaper_constants.js",
   ]
   namespace_rewrites = os_settings_namespace_rewrites
 }
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.js b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.js
index c4526d0..7af99d2 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.js
@@ -4,6 +4,7 @@
 
 // clang-format off
 // #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+// #import {WallpaperCollection} from './wallpaper_constants.m.js';
 // clang-format on
 
 cr.define('settings', function() {
@@ -21,6 +22,12 @@
     isWallpaperPolicyControlled() {}
 
     openWallpaperManager() {}
+
+    /**
+     * @return {!Promise<?Array<!WallpaperCollection>>} Returns a promise to
+     * an array of wallpaper collections. Will reject with null on error.
+     */
+    fetchWallpaperCollections() {}
   }
 
   /**
@@ -41,6 +48,11 @@
     openWallpaperManager() {
       chrome.send('openWallpaperManager');
     }
+
+    /** @override */
+    fetchWallpaperCollections() {
+      return cr.sendWithPromise('fetchWallpaperCollections');
+    }
   }
 
   cr.addSingletonGetter(WallpaperBrowserProxyImpl);
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.html b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.html
new file mode 100644
index 0000000..8915f5e
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.html
@@ -0,0 +1 @@
+<script src="wallpaper_constants.js"></script>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.js b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.js
new file mode 100644
index 0000000..cf76b1c
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_constants.js
@@ -0,0 +1,13 @@
+// Copyright 2021 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.
+
+/**
+ * WallpaperCollection describes a collection of wallpapers.
+ *
+ * @typedef {{
+ *   id: !string,
+ *   name: !string,
+ * }}
+ */
+/* #export */ let WallpaperCollection;
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.html b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.html
index 204a483..009cbc03 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.html
@@ -1,14 +1,37 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="../personalization_page/wallpaper_browser_proxy.html">
+<link rel="import" href="wallpaper_browser_proxy.html">
+<link rel="import" href="wallpaper_constants.html">
 <link rel="import" href="../../settings_shared_css.html">
 
 <dom-module id="settings-wallpaper-page">
   <template>
     <style include="cr-shared-style settings-shared">
+      paper-spinner-lite {
+        display: block;
+        height: 28px;
+        margin: 150px auto;
+        width: 28px;
+      }
     </style>
+    <paper-spinner-lite active="[[loading_]]"
+        hidden="[[!loading_]]">
+    </paper-spinner-lite>
+    <p hidden="[[!error_]]" id="error">$i18n{wallpaperCollectionsError}</p>
+    <template is="dom-if"
+        if="[[success_]]">
+      <iron-list items="[[collections_]]">
+        <template>
+          <p class="wallpaper-collection-title"
+              data-id$="[[item.id]]">
+            [[item.name]]
+          </p>
+        </template>
+      </iron-list>
+    </template>
   </template>
   <script src="wallpaper_subpage.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.js b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.js
index 5f23582..d197ee6 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_subpage.js
@@ -9,9 +9,44 @@
 Polymer({
   is: 'settings-wallpaper-page',
 
-  behaviors: [],
+  behaviors: [
+    I18nBehavior,
+  ],
 
-  properties: {},
+  properties: {
+    /**
+     * @private
+     * @type {?Array<!WallpaperCollection>}
+     */
+    collections_: {
+      type: Array,
+      value: null,
+    },
+
+    /** @private */
+    loading_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
+     * @private
+     * @type {boolean}
+     */
+    error_: {
+      type: Boolean,
+      computed: 'computeError_(collections_, loading_)',
+    },
+
+    /**
+     * @private
+     * @type {boolean}
+     */
+    success_: {
+      type: Boolean,
+      computed: 'computeSuccess_(collections_, loading_)',
+    },
+  },
 
   /** @private {?settings.WallpaperBrowserProxy} */
   browserProxy_: null,
@@ -21,5 +56,41 @@
     this.browserProxy_ = settings.WallpaperBrowserProxyImpl.getInstance();
   },
 
-  ready() {},
+  /** @override */
+  ready() {
+    this.fetchWallpaperCollections_();
+  },
+
+  /** @private */
+  async fetchWallpaperCollections_() {
+    this.loading_ = true;
+    this.collections_ = null;
+    try {
+      this.collections_ = await this.browserProxy_.fetchWallpaperCollections();
+    } catch (e) {
+      console.warn('Fetching wallpaper collections failed');
+    } finally {
+      this.loading_ = false;
+    }
+  },
+
+  /**
+   * @private
+   * @param {?Array<!WallpaperCollection>} collections
+   * @param {boolean} loading
+   * @return {boolean}
+   */
+  computeError_(collections, loading) {
+    return !loading && !this.computeSuccess_(collections, loading);
+  },
+
+  /**
+   * @private
+   * @param {?Array<!WallpaperCollection>} collections
+   * @param {boolean} loading
+   * @return {boolean}
+   */
+  computeSuccess_(collections, loading) {
+    return !loading && Array.isArray(collections) && collections.length > 0;
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/search_handler.html b/chrome/browser/resources/settings/chromeos/search_handler.html
index f8a5578..e7e30bf 100644
--- a/chrome/browser/resources/settings/chromeos/search_handler.html
+++ b/chrome/browser/resources/settings/chromeos/search_handler.html
@@ -1,8 +1,10 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/mojom/base/string16.mojom.html">
 
 <script src="chrome://os-settings/constants/routes.mojom-lite.js"></script>
 <script src="chrome://os-settings/constants/setting.mojom-lite.js"></script>
 <script src="chrome://os-settings/search/search_result_icon.mojom-lite.js"></script>
+<script src="chrome://os-settings/search/user_action_recorder.mojom-lite.js"></script>
 <script src="chrome://os-settings/search/search.mojom-lite.js"></script>
 <script src="search_handler.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/search_handler.js b/chrome/browser/resources/settings/chromeos/search_handler.js
index ae23ff9..a952e30 100644
--- a/chrome/browser/resources/settings/chromeos/search_handler.js
+++ b/chrome/browser/resources/settings/chromeos/search_handler.js
@@ -3,7 +3,12 @@
 // found in the LICENSE file.
 
 // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
 // #import '../constants/routes.mojom-lite.js';
+// #import '../constants/setting.mojom-lite.js';
+// #import '../search/search_result_icon.mojom-lite.js';
+// #import '../search/user_action_recorder.mojom-lite.js';
+// #import '../search/search.mojom-lite.js';
 
 /**
  * @fileoverview
diff --git a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.js b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.js
deleted file mode 100644
index 1c71824..0000000
--- a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.js
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('chrome.supervised_user_internals', function() {
-  'use strict';
-
-  function initialize() {
-    function submitURL(event) {
-      $('try-url-result').textContent = '';
-      $('manual-allowlist').textContent = '';
-      $('allowlists').textContent = '';
-      chrome.send('tryURL', [$('try-url-input').value]);
-      event.preventDefault();
-    }
-
-    $('try-url').addEventListener('submit', submitURL);
-
-    // Make the prototype jscontent element disappear.
-    jstProcess({}, $('filtering-results-container'));
-
-    chrome.send('registerForEvents');
-
-    chrome.send('getBasicInfo');
-  }
-
-  function highlightIfChanged(node, oldVal, newVal) {
-    function clearHighlight() {
-      this.removeAttribute('highlighted');
-    }
-
-    const oldStr = oldVal.toString();
-    const newStr = newVal.toString();
-    if (oldStr != '' && oldStr != newStr) {
-      // Note the addListener function does not end up creating duplicate
-      // listeners.  There can be only one listener per event at a time.
-      // See https://developer.mozilla.org/en/DOM/element.addEventListener
-      node.addEventListener('animationend', clearHighlight, false);
-      node.setAttribute('highlighted', '');
-    }
-  }
-
-  function receiveBasicInfo(info) {
-    jstProcess(new JsEvalContext(info), $('basic-info'));
-
-    // Hack: Schedule another refresh after a while.
-    setTimeout(function() {
-      chrome.send('getBasicInfo');
-    }, 5000);
-  }
-
-  function receiveUserSettings(settings) {
-    if (settings === null) {
-      $('user-settings').classList.add('hidden');
-      return;
-    }
-
-    $('user-settings').classList.remove('hidden');
-
-    // The user settings are returned as an object, flatten them into a
-    // list of key/value pairs for easier consumption by the HTML template.
-    // This is not done recursively, values are passed as their JSON
-    // representation.
-    const kvpairs = Object.keys(settings).map(function(key) {
-      return {key: key, value: JSON.stringify(settings[key], null, 2)};
-    });
-
-    jstProcess(new JsEvalContext({settings: kvpairs}), $('user-settings'));
-  }
-
-  function receiveTryURLResult(result) {
-    $('try-url-result').textContent = result['allowResult'];
-    $('manual-allowlist').textContent = result['manual'];
-    $('allowlists').textContent = result['allowlists'];
-  }
-
-  /**
-   * Helper to determine if an element is scrolled to its bottom limit.
-   * @param {Element} elem element to check
-   * @return {boolean} true if the element is scrolled to the bottom
-   */
-  function isScrolledToBottom(elem) {
-    return elem.scrollHeight - elem.scrollTop == elem.clientHeight;
-  }
-
-  /**
-   * Helper to scroll an element to its bottom limit.
-   * @param {Element} elem element to be scrolled
-   */
-  function scrollToBottom(elem) {
-    elem.scrollTop = elem.scrollHeight - elem.clientHeight;
-  }
-
-  /** Container for accumulated filtering results. */
-  const filteringResults = [];
-
-  /**
-   * Callback for incoming filtering results.
-   * @param {Object} result The result.
-   */
-  function receiveFilteringResult(result) {
-    filteringResults.push(result);
-
-    const container = $('filtering-results-container');
-
-    // Scroll to the bottom if we were already at the bottom.  Otherwise, leave
-    // the scrollbar alone.
-    const shouldScrollDown = isScrolledToBottom(container);
-
-    jstProcess(new JsEvalContext({results: filteringResults}), container);
-
-    if (shouldScrollDown) {
-      scrollToBottom(container);
-    }
-  }
-
-  // Return an object with all of the exports.
-  return {
-    initialize: initialize,
-    highlightIfChanged: highlightIfChanged,
-    receiveBasicInfo: receiveBasicInfo,
-    receiveUserSettings: receiveUserSettings,
-    receiveTryURLResult: receiveTryURLResult,
-    receiveFilteringResult: receiveFilteringResult,
-  };
-});
-
-document.addEventListener(
-    'DOMContentLoaded', chrome.supervised_user_internals.initialize);
diff --git a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
index be4c5f5b..29d036c 100644
--- a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
@@ -140,8 +140,7 @@
 }
 
 void SafeBrowsingMetricsCollector::OnEnhancedProtectionPrefChanged() {
-  if (safe_browsing::GetSafeBrowsingState(*pref_service_) !=
-      SafeBrowsingState::ENHANCED_PROTECTION) {
+  if (!pref_service_->GetBoolean(prefs::kSafeBrowsingEnhanced)) {
     LogEnhancedProtectionDisabledMetrics();
   }
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
index 4e3f8be7..b7b4023 100644
--- a/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
@@ -263,6 +263,11 @@
       "SafeBrowsing.EsbDisabled.LastBypassEventType",
       /* sample */ EventType::REAL_TIME_INTERSTITIAL_BYPASS,
       /* expected_count */ 1);
+
+  // Changing no protection to enhanced protection shouldn't log the metric.
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 2);
 }
 
 TEST_F(SafeBrowsingMetricsCollectorTest,
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index a1d2985b..10e2bcc 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -1137,13 +1137,13 @@
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL(kSingleFrameTestURL));
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
-  ClickTestLink("new_tab_download", 2, initial_url);
+  ClickTestLink("new_tab_download", 3, initial_url);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
   GURL blank_url = GURL(url::kAboutBlankURL);
   std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto* nav_list = navigation_event_list();
   ASSERT_TRUE(nav_list);
-  ASSERT_EQ(4U, nav_list->Size());
+  ASSERT_EQ(5U, nav_list->Size());
   VerifyNavigationEvent(GURL(),       // source_url
                         GURL(),       // source_main_frame_url
                         initial_url,  // original_request_url
@@ -1171,31 +1171,53 @@
                         false,      // has_server_redirect
                         nav_list->Get(2));
   EXPECT_EQ(nav_list->Get(2)->source_tab_id, nav_list->Get(2)->target_tab_id);
-  VerifyNavigationEvent(blank_url,     // source_url
-                        blank_url,     // source_main_frame_url
+  // This navigation is done by the document.write() call.
+  VerifyNavigationEvent(blank_url,    // source_url
+                        blank_url,    // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        false,        // is_user_initiated,
+                        false,        // has_committed
+                        false,        // has_server_redirect
+                        nav_list->Get(3));
+  VerifyNavigationEvent(initial_url,   // source_url
+                        initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
                         false,         // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
-                        nav_list->Get(3));
-  EXPECT_EQ(nav_list->Get(3)->source_tab_id, nav_list->Get(3)->target_tab_id);
+                        nav_list->Get(4));
+  EXPECT_EQ(nav_list->Get(4)->source_tab_id, nav_list->Get(3)->target_tab_id);
   VerifyHostToIpMap();
 
   ReferrerChain referrer_chain;
   IdentifyReferrerChainForDownload(GetDownload(), &referrer_chain);
-  ASSERT_EQ(3, referrer_chain.size());
+  ASSERT_EQ(4, referrer_chain.size());
   VerifyReferrerChainEntry(
       download_url,                   // url
       GURL(),                         // main_frame_url
       ReferrerChainEntry::EVENT_URL,  // type
       test_server_ip,                 // ip_address
-      blank_url,                      // referrer_url
+      initial_url,                    // referrer_url
       GURL(),                         // referrer_main_frame_url
       false,                          // is_retargeting
       std::vector<GURL>(),            // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
       referrer_chain.Get(0));
+  // This is the document.write() call, which changes the URL of the new tab to
+  // be the same as the initial URL.
+  VerifyReferrerChainEntry(
+      initial_url,                          // url
+      GURL(),                               // main_frame_url
+      ReferrerChainEntry::CLIENT_REDIRECT,  // type
+      test_server_ip,                       // ip_address
+      blank_url,                            // referrer_url
+      GURL(),                               // referrer_main_frame_url
+      false,                                // is_retargeting
+      std::vector<GURL>(),                  // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
+      referrer_chain.Get(1));
   VerifyReferrerChainEntry(
       blank_url,                            // url
       GURL(),                               // main_frame_url
@@ -1206,7 +1228,7 @@
       true,                                 // is_retargeting
       std::vector<GURL>(),                  // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
-      referrer_chain.Get(1));
+      referrer_chain.Get(2));
   VerifyReferrerChainEntry(initial_url,                       // url
                            GURL(),                            // main_frame_url
                            ReferrerChainEntry::LANDING_PAGE,  // type
@@ -1216,7 +1238,7 @@
                            false,                // is_retargeting
                            std::vector<GURL>(),  // server redirects
                            ReferrerChainEntry::BROWSER_INITIATED,
-                           referrer_chain.Get(2));
+                           referrer_chain.Get(3));
 }
 
 // Use javascript to open download in a new tab and download has a data url.
@@ -1225,14 +1247,14 @@
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL(kSingleFrameTestURL));
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
-  ClickTestLink("new_tab_download_with_data_url", 2, initial_url);
+  ClickTestLink("new_tab_download_with_data_url", 3, initial_url);
   GURL download_url = GURL(kDownloadDataURL);
   GURL short_download_url = GURL(kShortDataURL);
   GURL blank_url = GURL(url::kAboutBlankURL);
   std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto* nav_list = navigation_event_list();
   ASSERT_TRUE(nav_list);
-  ASSERT_EQ(4U, nav_list->Size());
+  ASSERT_EQ(5U, nav_list->Size());
   VerifyNavigationEvent(GURL(),       // source_url
                         GURL(),       // source_main_frame_url
                         initial_url,  // original_request_url
@@ -1261,33 +1283,54 @@
                         false,      // has_committed
                         false,      // has_server_redirect
                         nav_list->Get(2));
+  // This navigation is done by the document.write() call.
+  VerifyNavigationEvent(blank_url,    // source_url
+                        blank_url,    // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        false,        // is_user_initiated,
+                        false,        // has_committed
+                        false,        // has_server_redirect
+                        nav_list->Get(3));
   EXPECT_EQ(nav_list->Get(2)->source_tab_id, nav_list->Get(2)->target_tab_id);
-  VerifyNavigationEvent(blank_url,     // source_url
-                        blank_url,     // source_main_frame_url
+  VerifyNavigationEvent(initial_url,   // source_url
+                        initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
                         false,         // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
-                        nav_list->Get(3));
-  EXPECT_TRUE(nav_list->Get(3)->source_tab_id ==
-              nav_list->Get(3)->target_tab_id);
+                        nav_list->Get(4));
+  EXPECT_EQ(nav_list->Get(4)->source_tab_id, nav_list->Get(4)->target_tab_id);
   VerifyHostToIpMap();
 
   ReferrerChain referrer_chain;
   IdentifyReferrerChainForDownload(GetDownload(), &referrer_chain);
-  ASSERT_EQ(3, referrer_chain.size());
+  ASSERT_EQ(4, referrer_chain.size());
   VerifyReferrerChainEntry(
       short_download_url,             // url
       GURL(),                         // main_frame_url
       ReferrerChainEntry::EVENT_URL,  // type
       "",                             // ip_address
-      blank_url,                      // referrer_url
+      initial_url,                    // referrer_url
       GURL(),                         // referrer_main_frame_url
       false,                          // is_retargeting
       std::vector<GURL>(),            // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
       referrer_chain.Get(0));
+  // This is the document.write() call, which changes the URL of the new tab to
+  // be the same as the initial URL.
+  VerifyReferrerChainEntry(
+      initial_url,                          // url
+      GURL(),                               // main_frame_url
+      ReferrerChainEntry::CLIENT_REDIRECT,  // type
+      test_server_ip,                       // ip_address
+      blank_url,                            // referrer_url
+      GURL(),                               // referrer_main_frame_url
+      false,                                // is_retargeting
+      std::vector<GURL>(),                  // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
+      referrer_chain.Get(1));
   VerifyReferrerChainEntry(
       blank_url,                            // url
       GURL(),                               // main_frame_url
@@ -1298,7 +1341,7 @@
       true,                                 // is_retargeting
       std::vector<GURL>(),                  // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
-      referrer_chain.Get(1));
+      referrer_chain.Get(2));
   VerifyReferrerChainEntry(initial_url,                       // url
                            GURL(),                            // main_frame_url
                            ReferrerChainEntry::LANDING_PAGE,  // type
@@ -1308,7 +1351,7 @@
                            false,                // is_retargeting
                            std::vector<GURL>(),  // server redirects
                            ReferrerChainEntry::BROWSER_INITIATED,
-                           referrer_chain.Get(2));
+                           referrer_chain.Get(3));
 }
 
 // Click a link in a subframe and start download.
@@ -1452,13 +1495,13 @@
   GURL iframe_url = embedded_test_server()->GetURL(kIframeDirectDownloadURL);
   GURL iframe_retargeting_url =
       embedded_test_server()->GetURL(kIframeRetargetingURL);
-  ClickTestLink("iframe_new_tab_download", 2, iframe_retargeting_url, 1);
+  ClickTestLink("iframe_new_tab_download", 3, iframe_retargeting_url, 1);
   GURL blank_url = GURL(url::kAboutBlankURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
   std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto* nav_list = navigation_event_list();
   ASSERT_TRUE(nav_list);
-  ASSERT_EQ(7U, nav_list->Size());
+  ASSERT_EQ(8U, nav_list->Size());
   VerifyNavigationEvent(GURL(),       // source_url
                         GURL(),       // source_main_frame_url
                         initial_url,  // original_request_url
@@ -1528,30 +1571,53 @@
                         false,      // has_committed
                         false,      // has_server_redirect
                         nav_list->Get(5));
-  VerifyNavigationEvent(blank_url,     // source_url
-                        blank_url,     // source_main_frame_url
-                        download_url,  // original_request_url
-                        download_url,  // destination_url
-                        false,         // is_user_initiated,
-                        false,         // has_committed
-                        false,         // has_server_redirect
+  // This navigation is done by the document.write() call.
+  VerifyNavigationEvent(blank_url,               // source_url
+                        blank_url,               // source_main_frame_url
+                        iframe_retargeting_url,  // original_request_url
+                        iframe_retargeting_url,  // destination_url
+                        false,                   // is_user_initiated,
+                        false,                   // has_committed
+                        false,                   // has_server_redirect
                         nav_list->Get(6));
+  VerifyNavigationEvent(iframe_retargeting_url,  // source_url
+                        iframe_retargeting_url,  // source_main_frame_url
+                        download_url,            // original_request_url
+                        download_url,            // destination_url
+                        false,                   // is_user_initiated,
+                        false,                   // has_committed
+                        false,                   // has_server_redirect
+                        nav_list->Get(7));
   VerifyHostToIpMap();
 
   ReferrerChain referrer_chain;
   IdentifyReferrerChainForDownload(GetDownload(), &referrer_chain);
-  EXPECT_EQ(5, referrer_chain.size());
+  EXPECT_EQ(6, referrer_chain.size());
   VerifyReferrerChainEntry(
       download_url,                   // url
       GURL(),                         // main_frame_url
       ReferrerChainEntry::EVENT_URL,  // type
       test_server_ip,                 // ip_address
-      blank_url,                      // referrer_url
+      iframe_retargeting_url,         // referrer_url
       GURL(),                         // referrer_main_frame_url
       false,                          // is_retargeting
       std::vector<GURL>(),            // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
       referrer_chain.Get(0));
+  // This is the document.write() call, which changes the URL of the new tab to
+  // be the same as the URL of the document that called the function, which is
+  // iframe_retargeting.html.
+  VerifyReferrerChainEntry(
+      iframe_retargeting_url,               // url
+      GURL(),                               // main_frame_url
+      ReferrerChainEntry::CLIENT_REDIRECT,  // type
+      test_server_ip,                       // ip_address
+      blank_url,                            // referrer_url
+      GURL(),                               // referrer_main_frame_url
+      false,                                // is_retargeting
+      std::vector<GURL>(),                  // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
+      referrer_chain.Get(1));
   VerifyReferrerChainEntry(
       blank_url,                            // url
       GURL(),                               // main_frame_url
@@ -1562,7 +1628,7 @@
       true,                                 // is_retargeting
       std::vector<GURL>(),                  // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
-      referrer_chain.Get(1));
+      referrer_chain.Get(2));
   VerifyReferrerChainEntry(
       iframe_retargeting_url,            // url
       multi_frame_test_url,              // main_frame_url
@@ -1573,7 +1639,7 @@
       false,                             // is_retargeting
       std::vector<GURL>(),               // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
-      referrer_chain.Get(2));
+      referrer_chain.Get(3));
   VerifyReferrerChainEntry(
       multi_frame_test_url,                 // url
       GURL(),                               // main_frame_url
@@ -1584,7 +1650,7 @@
       false,                                // is_retargeting
       std::vector<GURL>(),                  // server redirects
       ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
-      referrer_chain.Get(3));
+      referrer_chain.Get(4));
   VerifyReferrerChainEntry(initial_url,  // url
                            GURL(),       // main_frame_url
                            ReferrerChainEntry::LANDING_REFERRER,  // type
@@ -1594,7 +1660,7 @@
                            false,                // is_retargeting
                            std::vector<GURL>(),  // server redirects
                            ReferrerChainEntry::BROWSER_INITIATED,
-                           referrer_chain.Get(4));
+                           referrer_chain.Get(5));
 }
 
 // Click a link which redirects to the landing page, and then click on the
diff --git a/chrome/browser/search/background/ntp_background.proto b/chrome/browser/search/background/ntp_background.proto
index 7aac003..a38b9a4 100644
--- a/chrome/browser/search/background/ntp_background.proto
+++ b/chrome/browser/search/background/ntp_background.proto
@@ -48,14 +48,15 @@
 }
 
 message GetCollectionsRequest {
-  // Deprecated or unused tag numbers
-  reserved 3;
-
   // The language that should be used for content. e.g. "en-US"
   optional string language = 1;
 
   // The approximate permanent location of the user. e.g. "us".
   optional string region = 2;
+
+  // Label (e.g. "chrome_desktop_ntp") used to return exclusive content or
+  // filter unwanted collections from the corpus.
+  repeated string filtering_label = 3;
 }
 
 message GetCollectionsResponse {
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index abe378d..85f1fc45 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -5,11 +5,13 @@
 #include "chrome/browser/search/background/ntp_background_service.h"
 
 #include "base/bind.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/search/background/ntp_background.pb.h"
 #include "chrome/browser/search/background/ntp_backgrounds.h"
+#include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/load_flags.h"
@@ -39,6 +41,9 @@
 // TODO(crbug.com/874339): Set options based on display resolution capability.
 constexpr char kImageOptions[] = "=w3840-h2160-p-k-no-nd-mv";
 
+// Label added to request to filter out unwanted collections.
+constexpr char kFilteringLabel[] = "chrome_desktop_ntp";
+
 }  // namespace
 
 NtpBackgroundService::NtpBackgroundService(
@@ -98,6 +103,11 @@
   ntp::background::GetCollectionsRequest request;
   // The language field may include the country code (e.g. "en-US").
   request.set_language(g_browser_process->GetApplicationLocale());
+  request.add_filtering_label(kFilteringLabel);
+  // Add some extra filtering information in case we need to target a specific
+  // milestone post release.
+  request.add_filtering_label(base::StrCat(
+      {kFilteringLabel, ".M", version_info::GetMajorVersionNumber()}));
   std::string serialized_proto;
   request.SerializeToString(&serialized_proto);
 
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc
index a5b8190..5ddb17f 100644
--- a/chrome/browser/search/background/ntp_background_service_unittest.cc
+++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -10,8 +10,11 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/search/background/ntp_background_data.h"
+#include "components/version_info/version_info.h"
 #include "content/public/test/browser_task_environment.h"
+#include "services/network/public/cpp/data_element.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,6 +55,9 @@
   }
 
   NtpBackgroundService* service() { return service_.get(); }
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
 
  private:
   // Required to run tests from UI and threads.
@@ -63,6 +69,29 @@
   std::unique_ptr<NtpBackgroundService> service_;
 };
 
+TEST_F(NtpBackgroundServiceTest, CorrectCollectionRequest) {
+  g_browser_process->SetApplicationLocale("foo");
+  service()->FetchCollectionInfo();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1u, test_url_loader_factory()->pending_requests()->size());
+  std::string request_body = test_url_loader_factory()
+                                 ->pending_requests()
+                                 ->at(0)
+                                 .request.request_body->elements()
+                                 ->at(0)
+                                 .As<network::DataElementBytes>()
+                                 .AsStringPiece()
+                                 .as_string();
+  ntp::background::GetCollectionsRequest collection_request;
+  EXPECT_TRUE(collection_request.ParseFromString(request_body));
+  EXPECT_EQ("foo", collection_request.language());
+  EXPECT_EQ(2, collection_request.filtering_label_size());
+  EXPECT_EQ("chrome_desktop_ntp", collection_request.filtering_label(0));
+  EXPECT_EQ("chrome_desktop_ntp.M" + version_info::GetMajorVersionNumber(),
+            collection_request.filtering_label(1));
+}
+
 TEST_F(NtpBackgroundServiceTest, CollectionInfoNetworkError) {
   SetUpResponseWithNetworkError(service()->GetCollectionsLoadURLForTesting());
 
diff --git a/chrome/browser/sharesheet/sharesheet_service.cc b/chrome/browser/sharesheet/sharesheet_service.cc
index 7c3bc62..36038814 100644
--- a/chrome/browser/sharesheet/sharesheet_service.cc
+++ b/chrome/browser/sharesheet/sharesheet_service.cc
@@ -194,7 +194,8 @@
       apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
                           WindowOpenDisposition::NEW_WINDOW,
                           /*prefer_container=*/true),
-      std::move(intent), launch_source, display::kDefaultDisplayId);
+      std::move(intent), launch_source,
+      apps::MakeWindowInfo(display::kDefaultDisplayId));
 }
 
 void SharesheetService::OnIconLoaded(
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCache.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCache.java
index 7f3d29c..252fce56 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCache.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCache.java
@@ -265,14 +265,9 @@
     @Override
     public void onExtendedAccountInfoUpdated(AccountInfo accountInfo) {
         final String accountEmail = accountInfo.getEmail();
-        DisplayableProfileData profileData = mCachedProfileData.get(accountEmail);
-        // if profileData is null, we will fetch monogram when generating
-        // the cache so that different sources will be handled in order.
-        if (profileData != null && profileData.getImage() == mPlaceholderImage) {
-            updateCachedProfileDataAndNotifyObservers(new DisplayableProfileData(accountEmail,
-                    prepareAvatar(accountInfo.getAccountImage(), accountEmail),
-                    profileData.getFullName(), profileData.getGivenName()));
-        }
+        updateCachedProfileDataAndNotifyObservers(new DisplayableProfileData(accountEmail,
+                prepareAvatar(accountInfo.getAccountImage(), accountEmail),
+                accountInfo.getFullName(), accountInfo.getGivenName()));
     }
 
     /**
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
index 5ea4988..95f91bd4 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
@@ -6,8 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
@@ -136,14 +134,8 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
-    public void testProfileDataUpdatedFromIdentityManager() throws IOException {
-        String accountEmail = "test@example.com";
-        when(mIdentityManagerNativeMock
-                        .findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress(
-                                anyLong(), eq(accountEmail)))
-                .thenReturn(new AccountInfo(new CoreAccountId("gaia-id-test"), accountEmail,
-                        "gaia-id-test", "full name", "given name", null));
-
+    public void testProfileDataUpdatedFromIdentityManagerObserver() throws IOException {
+        final String accountEmail = "test@example.com";
         mAccountManagerTestRule.addAccount(
                 new ProfileDataSource.ProfileData(accountEmail, null, "Full Name", "Given Name"));
         mIdentityManager.onExtendedAccountInfoUpdated(
@@ -156,6 +148,18 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    public void testProfileDataPopulatedFromIdentityManagerObserver() throws IOException {
+        final String accountEmail = "test@example.com";
+        mIdentityManager.onExtendedAccountInfoUpdated(
+                new AccountInfo(new CoreAccountId("gaia-id-test"), accountEmail, "gaia-id-test",
+                        "full name", "given name", createAvatar()));
+        TestThreadUtils.runOnUiThreadBlocking(() -> { checkImageIsScaled(accountEmail); });
+        mRenderTestRule.render(mImageView, "profile_data_cache_avatar" + mImageSize);
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
     public void testPlaceholderIsScaled() throws IOException {
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { checkImageIsScaled("no.data.for.this.account@example.com"); });
diff --git a/chrome/browser/speech/speech_recognition_service.cc b/chrome/browser/speech/chrome_speech_recognition_service.cc
similarity index 87%
rename from chrome/browser/speech/speech_recognition_service.cc
rename to chrome/browser/speech/chrome_speech_recognition_service.cc
index 075e6e99..d2faad1 100644
--- a/chrome/browser/speech/speech_recognition_service.cc
+++ b/chrome/browser/speech/chrome_speech_recognition_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/speech/speech_recognition_service.h"
+#include "chrome/browser/speech/chrome_speech_recognition_service.h"
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/soda_language_pack_component_installer.h"
@@ -22,15 +22,15 @@
 
 constexpr base::TimeDelta kIdleProcessTimeout = base::TimeDelta::FromSeconds(5);
 
-SpeechRecognitionService::SpeechRecognitionService(
+ChromeSpeechRecognitionService::ChromeSpeechRecognitionService(
     content::BrowserContext* context)
     : context_(context),
       enable_soda_(
           base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {}
 
-SpeechRecognitionService::~SpeechRecognitionService() = default;
+ChromeSpeechRecognitionService::~ChromeSpeechRecognitionService() = default;
 
-void SpeechRecognitionService::Create(
+void ChromeSpeechRecognitionService::Create(
     mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver) {
   LaunchIfNotRunning();
 
@@ -38,7 +38,7 @@
     speech_recognition_service_->BindContext(std::move(receiver));
 }
 
-void SpeechRecognitionService::OnNetworkServiceDisconnect() {
+void ChromeSpeechRecognitionService::OnNetworkServiceDisconnect() {
   if (!enable_soda_) {
     // If the Speech On-Device API
     // is not enabled, pass the URL
@@ -61,7 +61,7 @@
   }
 }
 
-void SpeechRecognitionService::LaunchIfNotRunning() {
+void ChromeSpeechRecognitionService::LaunchIfNotRunning() {
   if (speech_recognition_service_.is_bound())
     return;
 
@@ -74,7 +74,8 @@
     return;
 
   auto binary_path = global_prefs->GetFilePath(prefs::kSodaBinaryPath);
-  auto config_path = SpeechRecognitionService::GetSodaConfigPath(profile_prefs);
+  auto config_path =
+      ChromeSpeechRecognitionService::GetSodaConfigPath(profile_prefs);
   if (enable_soda_ && (binary_path.empty() || config_path.empty())) {
     LOG(ERROR) << "Unable to find SODA files on the device.";
     return;
@@ -96,15 +97,17 @@
 
   speech_recognition_service_client_.reset();
 
-  if (enable_soda_)
+  if (enable_soda_) {
     speech_recognition_service_->SetSodaPath(binary_path, config_path);
+  }
 
   speech_recognition_service_->BindSpeechRecognitionServiceClient(
       speech_recognition_service_client_.BindNewPipeAndPassRemote());
   OnNetworkServiceDisconnect();
 }
 
-base::FilePath SpeechRecognitionService::GetSodaConfigPath(PrefService* prefs) {
+base::FilePath ChromeSpeechRecognitionService::GetSodaConfigPath(
+    PrefService* prefs) {
   base::Optional<component_updater::SodaLanguagePackComponentConfig>
       language_config = component_updater::
           SodaLanguagePackComponentInstallerPolicy::GetLanguageComponentConfig(
@@ -114,7 +117,6 @@
     return g_browser_process->local_state()->GetFilePath(
         language_config.value().config_path_pref);
   }
-
   return base::FilePath();
 }
 }  // namespace speech
diff --git a/chrome/browser/speech/chrome_speech_recognition_service.h b/chrome/browser/speech/chrome_speech_recognition_service.h
new file mode 100644
index 0000000..d90f23b
--- /dev/null
+++ b/chrome/browser/speech/chrome_speech_recognition_service.h
@@ -0,0 +1,67 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_CHROME_SPEECH_RECOGNITION_SERVICE_H_
+#define CHROME_BROWSER_SPEECH_CHROME_SPEECH_RECOGNITION_SERVICE_H_
+
+#include "chrome/browser/speech/speech_recognition_service.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+class PrefService;
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace speech {
+
+// Provides a mojo endpoint in the browser that allows the renderer process to
+// launch and initialize the sandboxed speech recognition service
+// process.
+class ChromeSpeechRecognitionService
+    : public SpeechRecognitionService,
+      public media::mojom::SpeechRecognitionServiceClient {
+ public:
+  explicit ChromeSpeechRecognitionService(content::BrowserContext* context);
+  ChromeSpeechRecognitionService(const ChromeSpeechRecognitionService&) =
+      delete;
+  ChromeSpeechRecognitionService& operator=(const SpeechRecognitionService&) =
+      delete;
+  ~ChromeSpeechRecognitionService() override;
+
+  void Create(mojo::PendingReceiver<media::mojom::SpeechRecognitionContext>
+                  receiver) override;
+
+  // media::mojom::SpeechRecognitionServiceClient
+  void OnNetworkServiceDisconnect() override;
+
+ private:
+  // Launches the speech recognition service in a sandboxed utility process.
+  void LaunchIfNotRunning();
+
+  // Gets the path of the SODA configuration file for the selected language.
+  base::FilePath GetSodaConfigPath(PrefService* prefs);
+
+  // The browser context associated with the keyed service.
+  content::BrowserContext* const context_;
+
+  // A flag indicating whether to use the Speech On-Device API (SODA) for speech
+  // recognition.
+  const bool enable_soda_;
+
+  // The remote to the speech recognition service. The browser will not launch a
+  // new speech recognition service process if this remote is already bound.
+  mojo::Remote<media::mojom::SpeechRecognitionService>
+      speech_recognition_service_;
+
+  mojo::Receiver<media::mojom::SpeechRecognitionServiceClient>
+      speech_recognition_service_client_{this};
+};
+
+}  // namespace speech
+
+#endif  // CHROME_BROWSER_SPEECH_CHROME_SPEECH_RECOGNITION_SERVICE_H_
diff --git a/chrome/browser/speech/cros_speech_recognition_service.cc b/chrome/browser/speech/cros_speech_recognition_service.cc
new file mode 100644
index 0000000..c7dc56e9
--- /dev/null
+++ b/chrome/browser/speech/cros_speech_recognition_service.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/cros_speech_recognition_service.h"
+
+#include "chrome/services/speech/cros_speech_recognition_recognizer_impl.h"
+#include "components/soda/constants.h"
+#include "media/base/media_switches.h"
+
+namespace speech {
+
+CrosSpeechRecognitionService::CrosSpeechRecognitionService(
+    content::BrowserContext* context)
+    : ChromeSpeechRecognitionService(context),
+      enable_soda_(
+          base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {}
+
+CrosSpeechRecognitionService::~CrosSpeechRecognitionService() {}
+
+void CrosSpeechRecognitionService::Create(
+    mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver) {
+  if (enable_soda_) {
+    speech_recognition_contexts_.Add(this, std::move(receiver));
+  } else {
+    // If soda is not enabled, do the same thing as chrome.
+    ChromeSpeechRecognitionService::Create(std::move(receiver));
+  }
+}
+
+void CrosSpeechRecognitionService::BindRecognizer(
+    mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
+    mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
+    BindRecognizerCallback callback) {
+  // TODO(robsc): Create this with appropriate file locations.
+  CrosSpeechRecognitionRecognizerImpl::Create(
+      std::move(receiver), std::move(client), nullptr, base::FilePath(),
+      base::FilePath());
+  std::move(callback).Run(
+      CrosSpeechRecognitionRecognizerImpl::IsMultichannelSupported());
+}
+
+}  // namespace speech
diff --git a/chrome/browser/speech/cros_speech_recognition_service.h b/chrome/browser/speech/cros_speech_recognition_service.h
new file mode 100644
index 0000000..3da167b5
--- /dev/null
+++ b/chrome/browser/speech/cros_speech_recognition_service.h
@@ -0,0 +1,49 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_H_
+#define CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_H_
+
+#include "chrome/browser/speech/chrome_speech_recognition_service.h"
+#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace speech {
+
+// Provides a Mojo endpoint in the browser for the CROS system. This uses ML
+// Service, so is actually executing a little more in the
+// browser then regular chrome.
+class CrosSpeechRecognitionService
+    : public ChromeSpeechRecognitionService,
+      public media::mojom::SpeechRecognitionContext {
+ public:
+  explicit CrosSpeechRecognitionService(content::BrowserContext* context);
+  CrosSpeechRecognitionService(const CrosSpeechRecognitionService&) = delete;
+  CrosSpeechRecognitionService& operator=(const SpeechRecognitionService&) =
+      delete;
+  ~CrosSpeechRecognitionService() override;
+  void Create(mojo::PendingReceiver<media::mojom::SpeechRecognitionContext>
+                  receiver) override;
+
+  // media::mojom::SpeechRecognitionContext
+  void BindRecognizer(
+      mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
+      mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+          client,
+      BindRecognizerCallback callback) override;
+
+ private:
+  mojo::ReceiverSet<media::mojom::SpeechRecognitionContext>
+      speech_recognition_contexts_;
+  const bool enable_soda_;
+};
+
+}  // namespace speech
+
+#endif  // CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_H_
diff --git a/chrome/browser/speech/cros_speech_recognition_service_factory.cc b/chrome/browser/speech/cros_speech_recognition_service_factory.cc
new file mode 100644
index 0000000..ba9ff96b
--- /dev/null
+++ b/chrome/browser/speech/cros_speech_recognition_service_factory.cc
@@ -0,0 +1,46 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/speech/cros_speech_recognition_service.h"
+#include "chrome/browser/speech/speech_recognition_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+// static
+speech::SpeechRecognitionService*
+CrosSpeechRecognitionServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<speech::SpeechRecognitionService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+CrosSpeechRecognitionServiceFactory*
+CrosSpeechRecognitionServiceFactory::GetInstance() {
+  static base::NoDestructor<CrosSpeechRecognitionServiceFactory> instance;
+  return instance.get();
+}
+
+CrosSpeechRecognitionServiceFactory::CrosSpeechRecognitionServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "SpeechRecognitionService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+CrosSpeechRecognitionServiceFactory::~CrosSpeechRecognitionServiceFactory() =
+    default;
+
+KeyedService* CrosSpeechRecognitionServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new speech::CrosSpeechRecognitionService(context);
+}
+
+// Incognito profiles should use their own instance of the browser context.
+content::BrowserContext*
+CrosSpeechRecognitionServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
diff --git a/chrome/browser/speech/cros_speech_recognition_service_factory.h b/chrome/browser/speech/cros_speech_recognition_service_factory.h
new file mode 100644
index 0000000..616e4dd
--- /dev/null
+++ b/chrome/browser/speech/cros_speech_recognition_service_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace base {
+template <class T>
+class NoDestructor;
+}  // namespace base
+
+namespace speech {
+class SpeechRecognitionService;
+}  // namespace speech
+
+// Factory to get or create an instance of CrosSpeechRecognitionServiceFactory
+// from a Profile.
+class CrosSpeechRecognitionServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static speech::SpeechRecognitionService* GetForProfile(Profile* profile);
+
+ private:
+  friend class base::NoDestructor<CrosSpeechRecognitionServiceFactory>;
+  static CrosSpeechRecognitionServiceFactory* GetInstance();
+
+  CrosSpeechRecognitionServiceFactory();
+  ~CrosSpeechRecognitionServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_SPEECH_CROS_SPEECH_RECOGNITION_SERVICE_FACTORY_H_
diff --git a/chrome/browser/speech/cros_speech_recognition_service_factory_unittest.cc b/chrome/browser/speech/cros_speech_recognition_service_factory_unittest.cc
new file mode 100644
index 0000000..fe1159a
--- /dev/null
+++ b/chrome/browser/speech/cros_speech_recognition_service_factory_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace {
+
+using testing::IsNull;
+using testing::Not;
+
+// Verifies that the service factory supports incognito profiles.
+TEST(CrosSpeechRecognitionServiceFactoryTest, IncognitoProfile) {
+  content::BrowserTaskEnvironment task_environment;
+
+  TestingProfile profile;
+
+  const speech::SpeechRecognitionService* const service =
+      CrosSpeechRecognitionServiceFactory::GetForProfile(
+          profile.GetPrimaryOTRProfile());
+  EXPECT_THAT(service, Not(IsNull()));
+}
+
+}  // namespace
diff --git a/chrome/browser/speech/speech_recognition_service.h b/chrome/browser/speech/speech_recognition_service.h
index 41ad6beb..dfea36f 100644
--- a/chrome/browser/speech/speech_recognition_service.h
+++ b/chrome/browser/speech/speech_recognition_service.h
@@ -7,55 +7,15 @@
 
 #include "components/keyed_service/core/keyed_service.h"
 #include "media/mojo/mojom/speech_recognition_service.mojom.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-class PrefService;
-
-namespace content {
-class BrowserContext;
-}  // namespace content
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace speech {
 
-// Provides a mojo endpoint in the browser that allows the renderer process to
-// launch and initialize the sandboxed speech recognition service
-// process.
-class SpeechRecognitionService
-    : public KeyedService,
-      public media::mojom::SpeechRecognitionServiceClient {
+class SpeechRecognitionService : public KeyedService {
  public:
-  explicit SpeechRecognitionService(content::BrowserContext* context);
-  SpeechRecognitionService(const SpeechRecognitionService&) = delete;
-  SpeechRecognitionService& operator=(const SpeechRecognitionService&) = delete;
-  ~SpeechRecognitionService() override;
-
-  void Create(
-      mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver);
-
-  // media::mojom::SpeechRecognitionServiceClient
-  void OnNetworkServiceDisconnect() override;
-
- private:
-  // Launches the speech recognition service in a sandboxed utility process.
-  void LaunchIfNotRunning();
-
-  // Gets the path of the SODA configuration file for the selected language.
-  base::FilePath GetSodaConfigPath(PrefService* prefs);
-
-  // The browser context associated with the keyed service.
-  content::BrowserContext* const context_;
-
-  // A flag indicating whether to use the Speech On-Device API (SODA) for speech
-  // recognition.
-  bool enable_soda_ = false;
-
-  // The remote to the speech recognition service. The browser will not launch a
-  // new speech recognition service process if this remote is already bound.
-  mojo::Remote<media::mojom::SpeechRecognitionService>
-      speech_recognition_service_;
-
-  mojo::Receiver<media::mojom::SpeechRecognitionServiceClient>
-      speech_recognition_service_client_{this};
+  virtual void Create(
+      mojo::PendingReceiver<media::mojom::SpeechRecognitionContext>
+          receiver) = 0;
 };
 
 }  // namespace speech
diff --git a/chrome/browser/speech/speech_recognition_service_factory.cc b/chrome/browser/speech/speech_recognition_service_factory.cc
index 227263d..f3b390e 100644
--- a/chrome/browser/speech/speech_recognition_service_factory.cc
+++ b/chrome/browser/speech/speech_recognition_service_factory.cc
@@ -7,6 +7,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/speech/chrome_speech_recognition_service.h"
 #include "chrome/browser/speech/speech_recognition_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
@@ -33,7 +34,7 @@
 
 KeyedService* SpeechRecognitionServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new speech::SpeechRecognitionService(context);
+  return new speech::ChromeSpeechRecognitionService(context);
 }
 
 // Incognito profiles should use their own instance of the browser context.
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index fc6d7e4..95d82bb 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -287,9 +287,13 @@
                          name.c_str());
   // The executed scripts set the title to be the frame name when they have
   // finished loading.
+  content::TestNavigationObserver navigation_observer(GetWebContents(), 1);
   content::TitleWatcher title_watcher(GetWebContents(),
                                       base::ASCIIToUTF16(name));
   EXPECT_TRUE(content::ExecuteScript(rfh, script));
+  // The document.write() will implicitly call document.open(), which will send
+  // a same-document navigation notification. Wait for it.
+  navigation_observer.Wait();
   EXPECT_EQ(base::ASCIIToUTF16(name), title_watcher.WaitAndGetTitle());
   return content::FrameMatchingPredicate(
       content::WebContents::FromRenderFrameHost(rfh),
@@ -569,13 +573,18 @@
       CreateDocWrittenFrame(GetWebContents());
   EXPECT_FALSE(observer.GetIsAdSubframe(vanilla_frame->GetFrameTreeNodeId()));
 
+  // When the main frame invoked document.write() on |vanilla_frame|,
+  // |vanilla_frame|'s url was changed to match the main frame's url. Load
+  // |vanilla_frame|'s child frames with a query to ensure the urls are not
+  // identical, since outside of document.open() we refuse to navigate an iframe
+  // to its parent's url.
   content::RenderFrameHost* vanilla_child_of_vanilla =
-      CreateSrcFrame(vanilla_frame, GetURL("frame_factory.html"));
+      CreateSrcFrame(vanilla_frame, GetURL("frame_factory.html?1"));
   EXPECT_FALSE(
       observer.GetIsAdSubframe(vanilla_child_of_vanilla->GetFrameTreeNodeId()));
 
   content::RenderFrameHost* ad_child_of_vanilla =
-      CreateSrcFrameFromAdScript(vanilla_frame, GetURL("frame_factory.html"));
+      CreateSrcFrameFromAdScript(vanilla_frame, GetURL("frame_factory.html?2"));
   EXPECT_TRUE(
       observer.GetIsAdSubframe(ad_child_of_vanilla->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
@@ -591,7 +600,7 @@
       ScriptHeuristicEvidence::kCreatedByAdScript);
 
   content::RenderFrameHost* vanilla_child_of_ad =
-      CreateSrcFrame(ad_frame, GetURL("frame_factory.html"));
+      CreateSrcFrame(ad_frame, GetURL("frame_factory.html?3"));
   EXPECT_TRUE(
       observer.GetIsAdSubframe(vanilla_child_of_ad->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
@@ -600,7 +609,7 @@
       ScriptHeuristicEvidence::kCreatedByAdScript);
 
   content::RenderFrameHost* ad_child_of_ad =
-      CreateSrcFrameFromAdScript(ad_frame, GetURL("frame_factory.html"));
+      CreateSrcFrameFromAdScript(ad_frame, GetURL("frame_factory.html?4"));
   EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_of_ad->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
       ad_child_of_ad, /*parent_is_ad=*/true, FilterListEvidence::kNotChecked,
@@ -700,13 +709,19 @@
   content::RenderFrameHost* vanilla_frame_with_aborted_load =
       CreateFrameWithDocWriteAbortedLoad(GetWebContents());
 
+  // When the main frame invoked document.write() on
+  // |vanilla_frame_with_aborted_load|, |vanilla_frame_with_aborted_load|'s url
+  // was changed to match the main frame's url. Load
+  // |vanilla_frame_with_aborted_load|'s child frames with a query to ensure the
+  // urls are not identical, since outside of document.open() we refuse to
+  // navigate an iframe to its parent's url.
   content::RenderFrameHost* vanilla_child_of_vanilla = CreateSrcFrame(
-      vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
+      vanilla_frame_with_aborted_load, GetURL("frame_factory.html?1"));
   EXPECT_FALSE(
       observer.GetIsAdSubframe(vanilla_child_of_vanilla->GetFrameTreeNodeId()));
 
   content::RenderFrameHost* ad_child_of_vanilla = CreateSrcFrameFromAdScript(
-      vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
+      vanilla_frame_with_aborted_load, GetURL("frame_factory.html?2"));
   EXPECT_TRUE(
       observer.GetIsAdSubframe(ad_child_of_vanilla->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
@@ -725,8 +740,8 @@
       FilterListEvidence::kNotChecked,
       ScriptHeuristicEvidence::kCreatedByAdScript);
 
-  content::RenderFrameHost* vanilla_child_of_ad =
-      CreateSrcFrame(ad_frame_with_aborted_load, GetURL("frame_factory.html"));
+  content::RenderFrameHost* vanilla_child_of_ad = CreateSrcFrame(
+      ad_frame_with_aborted_load, GetURL("frame_factory.html?3"));
   EXPECT_TRUE(
       observer.GetIsAdSubframe(vanilla_child_of_ad->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
@@ -735,7 +750,7 @@
       ScriptHeuristicEvidence::kCreatedByAdScript);
 
   content::RenderFrameHost* ad_child_of_ad = CreateSrcFrameFromAdScript(
-      ad_frame_with_aborted_load, GetURL("frame_factory.html"));
+      ad_frame_with_aborted_load, GetURL("frame_factory.html?4"));
   EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_of_ad->GetFrameTreeNodeId()));
   observer.VerifyEvidenceForAdSubframe(
       ad_child_of_ad, /*parent_is_ad=*/true, FilterListEvidence::kNotChecked,
diff --git a/chrome/browser/tabmodel/BUILD.gn b/chrome/browser/tabmodel/BUILD.gn
index 9444bc1..2ed0b2939 100644
--- a/chrome/browser/tabmodel/BUILD.gn
+++ b/chrome/browser/tabmodel/BUILD.gn
@@ -34,6 +34,7 @@
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorFactory.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserver.java",
+    "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorSupplier.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabModelObserver.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java",
@@ -48,6 +49,7 @@
     "//components/embedder_support/android:util_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//ui/android:ui_full_java",
     "//url:gurl_java",
   ]
 }
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorSupplier.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorSupplier.java
new file mode 100644
index 0000000..7dddfcb7
--- /dev/null
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorSupplier.java
@@ -0,0 +1,29 @@
+// Copyright 2021 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.tabmodel;
+
+import org.chromium.base.UnownedUserDataKey;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.UnownedUserDataSupplier;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a
+ * {@link TabModelSelector}.
+ */
+public class TabModelSelectorSupplier extends UnownedUserDataSupplier<TabModelSelector> {
+    private static final UnownedUserDataKey<TabModelSelectorSupplier> KEY =
+            new UnownedUserDataKey<TabModelSelectorSupplier>(TabModelSelectorSupplier.class);
+
+    /** Return {@link TabModelSelector} supplier associated with the given {@link WindowAndroid}. */
+    public static ObservableSupplier<TabModelSelector> from(WindowAndroid windowAndroid) {
+        return KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
+    }
+
+    /** Constructs a TabModelSelectorSupplier and attaches it to the {@link WindowAndroid} */
+    public TabModelSelectorSupplier() {
+        super(KEY);
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index d8b3a415..ee2a0e1 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -19,7 +19,9 @@
 #include "chrome/browser/language/language_model_manager_factory.h"
 #include "chrome/browser/language/url_language_histogram_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/translate/translate_accept_languages_factory.h"
+#include "chrome/browser/translate/translate_model_service_factory.h"
 #include "chrome/browser/translate/translate_ranker_factory.h"
 #include "chrome/browser/translate/translate_service.h"
 #include "chrome/browser/ui/translate/translate_bubble_factory.h"
@@ -30,6 +32,7 @@
 #include "components/language/core/browser/language_model_manager.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/translate/content/browser/translate_model_service.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/browser/page_translated_details.h"
 #include "components/translate/core/browser/translate_accept_languages.h"
@@ -97,7 +100,10 @@
     translate_driver_ = std::make_unique<translate::ContentTranslateDriver>(
         &web_contents->GetController(),
         UrlLanguageHistogramFactory::GetForBrowserContext(
-            web_contents->GetBrowserContext()));
+            web_contents->GetBrowserContext()),
+        TranslateModelServiceFactory::GetOrBuildForKey(
+            Profile::FromBrowserContext(web_contents->GetBrowserContext())
+                ->GetProfileKey()));
   }
   translate_manager_ = std::make_unique<translate::TranslateManager>(
       this,
diff --git a/chrome/browser/translate/translate_model_service_browsertest.cc b/chrome/browser/translate/translate_model_service_browsertest.cc
index 7984dfe..107fd65 100644
--- a/chrome/browser/translate/translate_model_service_browsertest.cc
+++ b/chrome/browser/translate/translate_model_service_browsertest.cc
@@ -25,8 +25,10 @@
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/translate/core/common/translate_util.h"
+#include "components/translate/core/language_detection/language_detection_model.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -119,6 +121,15 @@
         {});
   }
 
+  void SetUp() override {
+    origin_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    origin_server_->ServeFilesFromSourceDirectory("chrome/test/data/translate");
+    ASSERT_TRUE(origin_server_->Start());
+    english_url_ = origin_server_->GetURL("/english_page.html");
+    InProcessBrowserTest::SetUp();
+  }
+
   ~TranslateModelServiceBrowserTest() override = default;
 
   translate::TranslateModelService* translate_model_service() {
@@ -126,8 +137,12 @@
         browser()->profile()->GetProfileKey());
   }
 
+  const GURL& english_url() const { return english_url_; }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  GURL english_url_;
+  std::unique_ptr<net::EmbeddedTestServer> origin_server_;
 };
 
 base::FilePath model_file_path() {
@@ -223,10 +238,33 @@
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
                        LanguageDetectionModelCreated) {
   base::HistogramTester histogram_tester;
-  ui_test_utils::NavigateToURL(browser(), GURL("https://test.com"));
+  ui_test_utils::NavigateToURL(browser(), english_url());
   RetryForHistogramUntilCountReached(
       &histogram_tester,
       "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 1);
   histogram_tester.ExpectUniqueSample(
       "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", false, 1);
 }
+
+IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
+                       LanguageDetectionModelAvailableForDetection) {
+  base::HistogramTester histogram_tester;
+  OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
+      ->OverrideTargetModelFileForTesting(
+          optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
+          /*model_metadata=*/base::nullopt, model_file_path());
+  RetryForHistogramUntilCountReached(
+      &histogram_tester,
+      "LanguageDetection.TFLiteModel.LanguageDetectionModelState", 1);
+  histogram_tester.ExpectUniqueSample(
+      "LanguageDetection.TFLiteModel.LanguageDetectionModelState",
+      translate::LanguageDetectionModelState::kModelFileValidAndMemoryMapped,
+      1);
+
+  ui_test_utils::NavigateToURL(browser(), english_url());
+  RetryForHistogramUntilCountReached(
+      &histogram_tester,
+      "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 1);
+  histogram_tester.ExpectBucketCount(
+      "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", true, 1);
+}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2eaa2857..ff832fd 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1638,10 +1638,10 @@
 
   if (enable_supervised_users) {
     sources += [
-      "webui/supervised_user_internals_message_handler.cc",
-      "webui/supervised_user_internals_message_handler.h",
-      "webui/supervised_user_internals_ui.cc",
-      "webui/supervised_user_internals_ui.h",
+      "webui/family_link_user_internals/family_link_user_internals_message_handler.cc",
+      "webui/family_link_user_internals/family_link_user_internals_message_handler.h",
+      "webui/family_link_user_internals/family_link_user_internals_ui.cc",
+      "webui/family_link_user_internals/family_link_user_internals_ui.h",
     ]
     deps += [ "//chrome/browser/supervised_user/supervised_user_error_page" ]
   }
@@ -2228,6 +2228,8 @@
       "webui/chromeos/cryptohome_ui.h",
       "webui/chromeos/cryptohome_web_ui_handler.cc",
       "webui/chromeos/cryptohome_web_ui_handler.h",
+      "webui/chromeos/diagnostics_dialog.cc",
+      "webui/chromeos/diagnostics_dialog.h",
       "webui/chromeos/drive_internals_ui.cc",
       "webui/chromeos/drive_internals_ui.h",
       "webui/chromeos/edu_account_login_handler_chromeos.cc",
@@ -3431,8 +3433,9 @@
       "autofill/payments/save_upi_bubble_controller.h",
       "autofill/payments/save_upi_bubble_controller_impl.cc",
       "autofill/payments/save_upi_bubble_controller_impl.h",
-      "autofill/save_address_profile_bubble_controller.cc",
       "autofill/save_address_profile_bubble_controller.h",
+      "autofill/save_address_profile_bubble_controller_impl.cc",
+      "autofill/save_address_profile_bubble_controller_impl.h",
       "bubble_anchor_util.h",
       "manifest_web_app_browser_controller.cc",
       "manifest_web_app_browser_controller.h",
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 f51e5ca..e7c63c64 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
@@ -90,7 +90,8 @@
                       WindowOpenDisposition::NEW_WINDOW,
                       false /* preferred_containner */),
                   apps::mojom::LaunchSource::kFromTest,
-                  display::Screen::GetScreen()->GetPrimaryDisplay().id());
+                  apps::MakeWindowInfo(
+                      display::Screen::GetScreen()->GetPrimaryDisplay().id()));
     app_loaded_observer.Wait();
   }
   EXPECT_TRUE(delegate->IsAppOpen(extension_app->id()));
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 38f9ff2..9c59074 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -13,6 +13,7 @@
 #include "base/notreached.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/remote_apps/remote_apps_manager.h"
 #include "chrome/browser/chromeos/remote_apps/remote_apps_manager_factory.h"
@@ -155,7 +156,7 @@
   apps::AppServiceProxy* proxy =
       apps::AppServiceProxyFactory::GetForProfile(profile());
   proxy->Launch(id(), event_flags, launch_source,
-                GetController()->GetAppListDisplayId());
+                apps::MakeWindowInfo(GetController()->GetAppListDisplayId()));
 }
 
 void AppServiceAppItem::CallLoadIcon(bool allow_placeholder_icon) {
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index f1b6e82..b685b3ed 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/apps/app_service/app_service_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/favicon/large_icon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
@@ -189,7 +190,7 @@
         controller()->GetAppListDisplayId());
   } else {
     proxy->Launch(app_id(), event_flags, launch_source,
-                  controller()->GetAppListDisplayId());
+                  apps::MakeWindowInfo(controller()->GetAppListDisplayId()));
   }
 }
 
diff --git a/chrome/browser/ui/app_list/search/help_app_provider.cc b/chrome/browser/ui/app_list/search/help_app_provider.cc
index 9c034ae7..fb9326c 100644
--- a/chrome/browser/ui/app_list/search/help_app_provider.cc
+++ b/chrome/browser/ui/app_list/search/help_app_provider.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/user_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_id_constants.h"
@@ -56,7 +57,7 @@
   proxy->LaunchAppWithUrl(web_app::kHelpAppId, event_flags,
                           GURL("chrome://help-app/updates"),
                           apps::mojom::LaunchSource::kFromAppListRecommendation,
-                          display::kDefaultDisplayId);
+                          apps::MakeWindowInfo(display::kDefaultDisplayId));
   chromeos::ReleaseNotesStorage(profile_).StopShowingSuggestionChip();
 }
 
diff --git a/chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h b/chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h
index 7cd643a6..3187357 100644
--- a/chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h
+++ b/chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h
@@ -16,10 +16,38 @@
 
 namespace app_list {
 
-// The launcher takes scores from providers, these all have different
-// distributions and ranges, which makes them difficult to compare. Here we have
-// implemented a way to normalize ChromeSearchResults so relevance scores can be
-// compared for the launcher.
+// ScoreNormalizer is responsible for normalizing relevance scores of search
+// results from providers. It learns to transform all scores to a uniformly
+// distributed normalized score between 0 to 1. The launcher takes scores of
+// results from many providers, these all have different distributions and
+// ranges, which makes them difficult to compare. After normalizing the
+// relevance scores can be compared and ranked in the launcher. This class
+// should only be initialized in a provider class where it can be called to
+// record and normalize scores.
+//
+// To normalize scores the ScoreNormalizer uses a BalancedReservoir. The
+// BalancedReservoir stores a subset of the search result scores, this subset of
+// scores is called dividers. To normalize a score, the quantile of that score
+// in the dividers is returned, this is always a value in (0,1). If a normalized
+// score of 1 is returned, this is either due to empty dividers or index out of
+// range when finding which index the score is in the dividers. The dividers are
+// updated such that the counts of scores observed between each divider remains
+// balanced. Each pair of adjacent dividers forms a histogram bin. To ensure
+// bins remain balanced with each new score added bins are:
+// 1. Split by adding the new score in between dividers.
+// 2. Smallest bins are then merged.
+// The L2 error is calculated to ensure splits and merges improve the balance of
+// the reservoir.
+//
+// Use of the ScoreNormalizer:
+// - Initialization. A profile is required since information about the provider
+// scores in the BalancedReservoir class are stored in prefs. The reservoir size
+// can be set to 25, or any other positive integer.
+// - RecordResults() should be called right before new results are swapped in
+// for old results in the providers. This updates the BalancedReservoir with the
+// provider's score distribution.
+// - NormalizeResults() should then be called. This changes the relevance scores
+// of the ChromeSearchResult in place with the normalized score.
 class ScoreNormalizer {
  public:
   using Results = std::vector<std::unique_ptr<ChromeSearchResult>>;
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index c7bec7c..8d9d964 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -73,7 +73,6 @@
 #include "ui/aura/window.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/display/types/display_constants.h"
 #include "url/url_constants.h"
 
 using arc::mojom::ChromePage;
@@ -374,7 +373,7 @@
         apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerNone,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
                             /*preferred_containner=*/true),
-        apps::mojom::LaunchSource::kFromKeyboard, display::kInvalidDisplayId);
+        apps::mojom::LaunchSource::kFromKeyboard);
   };
 
   bool result = proxy->AppRegistryCache().ForOneApp(
@@ -538,8 +537,7 @@
   apps::AppServiceProxy* proxy =
       apps::AppServiceProxyFactory::GetForProfile(profile);
   proxy->LaunchAppWithUrl(*app_id, event_flags, url,
-                          apps::mojom::LaunchSource::kFromArc,
-                          display::kInvalidDisplayId);
+                          apps::mojom::LaunchSource::kFromArc);
 
   chromeos::ApkWebAppService* apk_web_app_service =
       chromeos::ApkWebAppService::Get(profile);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 7985b3b1..a76289f 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -289,7 +289,8 @@
     proxy->FlushMojoCallsForTesting();
     proxy->Launch(extension->id(), event_flags,
                   apps::mojom::LaunchSource::kFromTest,
-                  display::Screen::GetScreen()->GetPrimaryDisplay().id());
+                  apps::MakeWindowInfo(
+                      display::Screen::GetScreen()->GetPrimaryDisplay().id()));
     proxy->FlushMojoCallsForTesting();
     return extension;
   }
@@ -1618,7 +1619,8 @@
                           WindowOpenDisposition::NEW_FOREGROUND_TAB,
                           true /* prefer_containner */),
       apps::mojom::LaunchSource::kFromTest,
-      display::Screen::GetScreen()->GetPrimaryDisplay().id());
+      apps::MakeWindowInfo(
+          display::Screen::GetScreen()->GetPrimaryDisplay().id()));
   proxy->FlushMojoCallsForTesting();
 
   // A new browser should get detected and one more should be running.
@@ -2211,7 +2213,8 @@
                           WindowOpenDisposition::NEW_FOREGROUND_TAB,
                           true /* prefer_containner */),
       apps::mojom::LaunchSource::kFromTest,
-      display::Screen::GetScreen()->GetPrimaryDisplay().id());
+      apps::MakeWindowInfo(
+          display::Screen::GetScreen()->GetPrimaryDisplay().id()));
   EXPECT_EQ(ash::STATUS_RUNNING, shelf_model()->ItemByID(id)->status);
 
   // Find the browser which holds our app.
diff --git a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
index 4f7dd08..fa5e823 100644
--- a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
@@ -256,7 +257,8 @@
   // Launch apps with AppServiceProxy.Launch.
   if (proxy->AppRegistryCache().GetAppType(app_id) !=
       apps::mojom::AppType::kUnknown) {
-    proxy->Launch(app_id, event_flags, ConvertLaunchSource(source), display_id);
+    proxy->Launch(app_id, event_flags, ConvertLaunchSource(source),
+                  apps::MakeWindowInfo(display_id));
     return;
   }
 
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index b3f1b8b3..e674f4cf 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -14,14 +14,15 @@
 #include "base/metrics/user_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/help_app_launcher.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/set_time_dialog.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -323,7 +324,7 @@
   proxy->LaunchAppWithUrl(web_app::kHelpAppId, ui::EventFlags::EF_NONE,
                           GURL(chrome::kChromeOSGestureEducationHelpURL),
                           apps::mojom::LaunchSource::kFromOtherApp,
-                          display::kDefaultDisplayId);
+                          apps::MakeWindowInfo(display::kDefaultDisplayId));
 }
 
 void SystemTrayClient::ShowPaletteHelp() {
diff --git a/chrome/browser/ui/ash/system_tray_client.h b/chrome/browser/ui/ash/system_tray_client.h
index 8fbd329..952d7f8 100644
--- a/chrome/browser/ui/ash/system_tray_client.h
+++ b/chrome/browser/ui/ash/system_tray_client.h
@@ -7,7 +7,7 @@
 
 #include "ash/public/cpp/system_tray_client.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system/system_clock_observer.h"
+#include "chrome/browser/ash/system/system_clock_observer.h"
 #include "chrome/browser/upgrade_detector/upgrade_observer.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.cc b/chrome/browser/ui/ash/wallpaper_controller_client.cc
index e5e2f8e..7f12fce 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.cc
@@ -33,7 +33,6 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#include "ui/display/types/display_constants.h"
 
 namespace {
 
@@ -501,8 +500,7 @@
       apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
                           WindowOpenDisposition::NEW_WINDOW,
                           false /* preferred_containner */),
-      apps::mojom::LaunchSource::kFromChromeInternal,
-      display::kInvalidDisplayId);
+      apps::mojom::LaunchSource::kFromChromeInternal);
 }
 
 void WallpaperControllerClient::MaybeClosePreviewWallpaper() {
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 71aab1ce0..4fa6a5d 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -31,7 +31,7 @@
 #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/create_card_unmask_prompt_view.h"
 #include "chrome/browser/ui/autofill/payments/credit_card_scanner_controller.h"
-#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
@@ -524,9 +524,9 @@
 #if defined(OS_ANDROID)
   // TODO(crbug.com/1167061): Implement.
 #else
-  SaveAddressProfileBubbleController::CreateForWebContents(web_contents());
-  SaveAddressProfileBubbleController* controller =
-      SaveAddressProfileBubbleController::FromWebContents(web_contents());
+  SaveAddressProfileBubbleControllerImpl::CreateForWebContents(web_contents());
+  SaveAddressProfileBubbleControllerImpl* controller =
+      SaveAddressProfileBubbleControllerImpl::FromWebContents(web_contents());
   controller->OfferSave(profile, std::move(callback));
 #endif
 }
@@ -637,6 +637,11 @@
     popup_controller_->Hide(reason);
 }
 
+void ChromeAutofillClient::ShowOfferNotificationIfApplicable(
+    const std::vector<GURL>& domains_to_display_bubble) {
+  // TODO(crbug.com/1093057): Finish bubble controller/view and wire it up.
+}
+
 bool ChromeAutofillClient::IsAutocompleteEnabled() {
   return prefs::IsAutocompleteEnabled(GetPrefs());
 }
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 4176595..b77d322e 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -144,6 +144,8 @@
   void UpdatePopup(const std::vector<Suggestion>& suggestions,
                    PopupType popup_type) override;
   void HideAutofillPopup(PopupHidingReason reason) override;
+  void ShowOfferNotificationIfApplicable(
+      const std::vector<GURL>& domains_to_display_bubble) override;
   bool IsAutocompleteEnabled() override;
   void PropagateAutofillPredictions(
       content::RenderFrameHost* rfh,
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller.h b/chrome/browser/ui/autofill/save_address_profile_bubble_controller.h
index 4251312..1c7bc4f4 100644
--- a/chrome/browser/ui/autofill/save_address_profile_bubble_controller.h
+++ b/chrome/browser/ui/autofill/save_address_profile_bubble_controller.h
@@ -1,62 +1,24 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CHROME_BROWSER_UI_AUTOFILL_SAVE_ADDRESS_PROFILE_BUBBLE_CONTROLLER_H_
 #define CHROME_BROWSER_UI_AUTOFILL_SAVE_ADDRESS_PROFILE_BUBBLE_CONTROLLER_H_
 
-#include "base/strings/string16.h"
-#include "chrome/browser/ui/autofill/autofill_bubble_controller_base.h"
 #include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "content/public/browser/web_contents_user_data.h"
 
 namespace autofill {
 
-class SaveAddressProfileView;
-
-// The controller functionality for SaveAddressProfileView.
-class SaveAddressProfileBubbleController
-    : public AutofillBubbleControllerBase,
-      public content::WebContentsUserData<SaveAddressProfileBubbleController> {
+// Interface that exposes controller functionality to SaveAddressProfileView
+// bubble.
+class SaveAddressProfileBubbleController {
  public:
-  SaveAddressProfileBubbleController(
-      const SaveAddressProfileBubbleController&) = delete;
-  SaveAddressProfileBubbleController& operator=(
-      const SaveAddressProfileBubbleController&) = delete;
-  ~SaveAddressProfileBubbleController() override;
+  virtual ~SaveAddressProfileBubbleController() = default;
 
-  base::string16 GetWindowTitle() const;
-
-  // Sets up the controller and offers to save the |profile|.
-  // |address_profile_save_prompt_callback| will be invoked once the user makes
-  // a decision with respect to the offer-to-save prompt.
-  void OfferSave(const AutofillProfile& profile,
-                 AutofillClient::AddressProfileSavePromptCallback
-                     address_profile_save_prompt_callback);
-
-  void OnBubbleClosed();
-
- protected:
-  // AutofillBubbleControllerBase::
-  PageActionIconType GetPageActionIconType() override;
-  void DoShowBubble() override;
-
- private:
-  explicit SaveAddressProfileBubbleController(
-      content::WebContents* web_contents);
-  friend class content::WebContentsUserData<SaveAddressProfileBubbleController>;
-
-  // Callback to run once the user makes a decision with respect to the saving
-  // the address profile.
-  AutofillClient::AddressProfileSavePromptCallback
-      address_profile_save_prompt_callback_;
-
-  // Contains the details of the address profile that will be saved if the user
-  // accepts.
-  AutofillProfile address_profile_;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
+  virtual base::string16 GetWindowTitle() const = 0;
+  virtual void OnUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision decision) = 0;
+  virtual void OnBubbleClosed() = 0;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller.cc b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.cc
similarity index 66%
rename from chrome/browser/ui/autofill/save_address_profile_bubble_controller.cc
rename to chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.cc
index 18c7ffd..5acb9d7 100644
--- a/chrome/browser/ui/autofill/save_address_profile_bubble_controller.cc
+++ b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h"
 
 #include "chrome/browser/ui/autofill/autofill_bubble_handler.h"
 #include "chrome/browser/ui/browser.h"
@@ -12,21 +12,17 @@
 
 namespace autofill {
 
-SaveAddressProfileBubbleController::SaveAddressProfileBubbleController(
+SaveAddressProfileBubbleControllerImpl::SaveAddressProfileBubbleControllerImpl(
     content::WebContents* web_contents)
     : AutofillBubbleControllerBase(web_contents) {
   DCHECK(base::FeatureList::IsEnabled(
       features::kAutofillAddressProfileSavePrompt));
 }
 
-SaveAddressProfileBubbleController::~SaveAddressProfileBubbleController() =
-    default;
+SaveAddressProfileBubbleControllerImpl::
+    ~SaveAddressProfileBubbleControllerImpl() = default;
 
-base::string16 SaveAddressProfileBubbleController::GetWindowTitle() const {
-  return base::string16();
-}
-
-void SaveAddressProfileBubbleController::OfferSave(
+void SaveAddressProfileBubbleControllerImpl::OfferSave(
     const AutofillProfile& profile,
     AutofillClient::AddressProfileSavePromptCallback
         address_profile_save_prompt_callback) {
@@ -39,16 +35,29 @@
   Show();
 }
 
-void SaveAddressProfileBubbleController::OnBubbleClosed() {
+base::string16 SaveAddressProfileBubbleControllerImpl::GetWindowTitle() const {
+  return base::string16();
+}
+
+void SaveAddressProfileBubbleControllerImpl::OnUserDecision(
+    AutofillClient::SaveAddressProfileOfferUserDecision decision) {
+  set_bubble_view(nullptr);
+
+  std::move(address_profile_save_prompt_callback_)
+      .Run(decision, address_profile_);
+}
+
+void SaveAddressProfileBubbleControllerImpl::OnBubbleClosed() {
   set_bubble_view(nullptr);
   UpdatePageActionIcon();
 }
 
-PageActionIconType SaveAddressProfileBubbleController::GetPageActionIconType() {
+PageActionIconType
+SaveAddressProfileBubbleControllerImpl::GetPageActionIconType() {
   return PageActionIconType::kSaveCard;
 }
 
-void SaveAddressProfileBubbleController::DoShowBubble() {
+void SaveAddressProfileBubbleControllerImpl::DoShowBubble() {
   DCHECK(!bubble_view());
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   set_bubble_view(
@@ -59,6 +68,6 @@
   DCHECK(bubble_view());
 }
 
-WEB_CONTENTS_USER_DATA_KEY_IMPL(SaveAddressProfileBubbleController)
+WEB_CONTENTS_USER_DATA_KEY_IMPL(SaveAddressProfileBubbleControllerImpl)
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h
new file mode 100644
index 0000000..1c1e83b
--- /dev/null
+++ b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h
@@ -0,0 +1,70 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_AUTOFILL_SAVE_ADDRESS_PROFILE_BUBBLE_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_AUTOFILL_SAVE_ADDRESS_PROFILE_BUBBLE_CONTROLLER_IMPL_H_
+
+#include "base/strings/string16.h"
+#include "chrome/browser/ui/autofill/autofill_bubble_controller_base.h"
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace autofill {
+
+class SaveAddressProfileView;
+
+// The controller functionality for SaveAddressProfileView.
+class SaveAddressProfileBubbleControllerImpl
+    : public AutofillBubbleControllerBase,
+      public SaveAddressProfileBubbleController,
+      public content::WebContentsUserData<
+          SaveAddressProfileBubbleControllerImpl> {
+ public:
+  SaveAddressProfileBubbleControllerImpl(
+      const SaveAddressProfileBubbleControllerImpl&) = delete;
+  SaveAddressProfileBubbleControllerImpl& operator=(
+      const SaveAddressProfileBubbleControllerImpl&) = delete;
+  ~SaveAddressProfileBubbleControllerImpl() override;
+
+  // Sets up the controller and offers to save the |profile|.
+  // |address_profile_save_prompt_callback| will be invoked once the user makes
+  // a decision with respect to the offer-to-save prompt.
+  void OfferSave(const AutofillProfile& profile,
+                 AutofillClient::AddressProfileSavePromptCallback
+                     address_profile_save_prompt_callback);
+
+  // SaveAddressProfileBubbleController:
+  base::string16 GetWindowTitle() const override;
+  void OnUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision decision) override;
+  void OnBubbleClosed() override;
+
+ protected:
+  // AutofillBubbleControllerBase:
+  PageActionIconType GetPageActionIconType() override;
+  void DoShowBubble() override;
+
+ private:
+  explicit SaveAddressProfileBubbleControllerImpl(
+      content::WebContents* web_contents);
+  friend class content::WebContentsUserData<
+      SaveAddressProfileBubbleControllerImpl>;
+
+  // Callback to run once the user makes a decision with respect to the saving
+  // the address profile.
+  AutofillClient::AddressProfileSavePromptCallback
+      address_profile_save_prompt_callback_;
+
+  // Contains the details of the address profile that will be saved if the user
+  // accepts.
+  AutofillProfile address_profile_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_AUTOFILL_SAVE_ADDRESS_PROFILE_BUBBLE_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_browsertest.cc b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_browsertest.cc
similarity index 74%
rename from chrome/browser/ui/autofill/save_address_profile_bubble_controller_browsertest.cc
rename to chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_browsertest.cc
index b436d43..f6fa2d4 100644
--- a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_browsertest.cc
+++ b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h"
 
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/browser.h"
@@ -14,9 +14,9 @@
 
 namespace autofill {
 
-class SaveAddressProfileBubbleControllerTest : public DialogBrowserTest {
+class SaveAddressProfileBubbleControllerImplTest : public DialogBrowserTest {
  public:
-  SaveAddressProfileBubbleControllerTest() {
+  SaveAddressProfileBubbleControllerImplTest() {
     feature_list_.InitAndEnableFeature(
         features::kAutofillAddressProfileSavePrompt);
   }
@@ -34,29 +34,30 @@
     autofill_client->ConfirmSaveAddressProfile(test::GetFullProfile(),
                                                base::DoNothing());
     controller_ =
-        SaveAddressProfileBubbleController::FromWebContents(web_contents);
+        SaveAddressProfileBubbleControllerImpl::FromWebContents(web_contents);
     DCHECK(controller_);
   }
 
-  SaveAddressProfileBubbleController* controller() { return controller_; }
+  SaveAddressProfileBubbleControllerImpl* controller() { return controller_; }
 
  private:
-  SaveAddressProfileBubbleController* controller_ = nullptr;
+  SaveAddressProfileBubbleControllerImpl* controller_ = nullptr;
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerTest, InvokeUi_Save) {
+IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerImplTest,
+                       InvokeUi_Save) {
   ShowAndVerifyUi();
 }
 
-IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerTest,
+IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerImplTest,
                        InvokeUi_SaveCloseThenReopen) {
   ShowAndVerifyUi();
   controller()->OnBubbleClosed();
   ShowAndVerifyUi();
 }
 
-IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerTest,
+IN_PROC_BROWSER_TEST_F(SaveAddressProfileBubbleControllerImplTest,
                        CloseTabWhileBubbleIsOpen) {
   ShowAndVerifyUi();
   content::WebContents* tab =
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_unittest.cc
new file mode 100644
index 0000000..edc8f1af
--- /dev/null
+++ b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller_impl.h"
+
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class SaveAddressProfileBubbleControllerImplTest
+    : public BrowserWithTestWindowTest {
+ public:
+  SaveAddressProfileBubbleControllerImplTest() = default;
+  void SetUp() override {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(
+        features::kAutofillAddressProfileSavePrompt);
+
+    BrowserWithTestWindowTest::SetUp();
+    AddTab(browser(), GURL("about:blank"));
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    SaveAddressProfileBubbleControllerImpl::CreateForWebContents(web_contents);
+  }
+
+  SaveAddressProfileBubbleControllerImpl* controller() {
+    return SaveAddressProfileBubbleControllerImpl::FromWebContents(
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+};
+
+TEST_F(SaveAddressProfileBubbleControllerImplTest,
+       DialogAcceptedInvokesCallback) {
+  AutofillProfile profile = test::GetFullProfile();
+  base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
+  controller()->OfferSave(profile, callback.Get());
+
+  EXPECT_CALL(
+      callback,
+      Run(AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted,
+          profile));
+  controller()->OnUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted);
+}
+
+TEST_F(SaveAddressProfileBubbleControllerImplTest,
+       DialogCancelledInvokesCallback) {
+  AutofillProfile profile = test::GetFullProfile();
+  base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
+  controller()->OfferSave(profile, callback.Get());
+
+  EXPECT_CALL(
+      callback,
+      Run(AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined,
+          testing::_));
+  controller()->OnUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_unittest.cc b/chrome/browser/ui/autofill/save_address_profile_bubble_controller_unittest.cc
deleted file mode 100644
index c692076..0000000
--- a/chrome/browser/ui/autofill/save_address_profile_bubble_controller_unittest.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "chrome/test/base/browser_with_test_window_test.h"
-#include "components/autofill/core/common/autofill_features.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-
-class SaveAddressProfileBubbleControllerTest
-    : public BrowserWithTestWindowTest {
- public:
-  SaveAddressProfileBubbleControllerTest() = default;
-  void SetUp() override {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        features::kAutofillAddressProfileSavePrompt);
-
-    BrowserWithTestWindowTest::SetUp();
-    AddTab(browser(), GURL("about:blank"));
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    SaveAddressProfileBubbleController::CreateForWebContents(web_contents);
-  }
-};
-
-TEST_F(SaveAddressProfileBubbleControllerTest, NoCrash) {}
-
-}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
index 239daf9..5fc1a22 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
@@ -43,8 +43,9 @@
     content::WebContents* contents,
     SaveAddressProfileBubbleController* controller,
     bool is_user_gesture) {
-  NOTIMPLEMENTED();
-  return nullptr;
+  if (!save_address_profile_bubble_view_)
+    save_address_profile_bubble_view_ = std::make_unique<TestAutofillBubble>();
+  return save_address_profile_bubble_view_.get();
 }
 
 void TestAutofillBubbleHandler::OnPasswordSaved() {}
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
index 9537a36..693c614 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
@@ -49,6 +49,7 @@
   std::unique_ptr<TestAutofillBubble> local_card_migration_bubble_view_;
   std::unique_ptr<TestAutofillBubble> save_card_bubble_view_;
   std::unique_ptr<TestSaveUPIBubble> save_upi_bubble_;
+  std::unique_ptr<TestAutofillBubble> save_address_profile_bubble_view_;
 
   DISALLOW_COPY_AND_ASSIGN(TestAutofillBubbleHandler);
 };
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 1921076..e640dcc 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -842,6 +842,7 @@
     // UI debug commands
     case IDC_DEBUG_TOGGLE_TABLET_MODE:
     case IDC_DEBUG_PRINT_VIEW_TREE:
+    case IDC_DEBUG_PRINT_VIEW_TREE_DETAILS:
       ExecuteUIDebugCommand(id, browser_);
       break;
 
@@ -1140,6 +1141,8 @@
   if (base::FeatureList::IsEnabled(features::kUIDebugTools)) {
     command_updater_.UpdateCommandEnabled(IDC_DEBUG_TOGGLE_TABLET_MODE, true);
     command_updater_.UpdateCommandEnabled(IDC_DEBUG_PRINT_VIEW_TREE, true);
+    command_updater_.UpdateCommandEnabled(IDC_DEBUG_PRINT_VIEW_TREE_DETAILS,
+                                          true);
   }
 
   // Initialize other commands whose state changes based on various conditions.
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index 027153e..4afbc64 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/browser_navigator_browsertest.h"
-#include "content/public/test/browser_test.h"
 
 #include <memory>
 
@@ -46,9 +45,11 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/bindings_policy.h"
+#include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/resource_request_body.h"
 
@@ -1368,9 +1369,7 @@
 }
 
 // This test makes sure a crashed singleton tab reloads from a new navigation.
-// TODO(https://crbug.com/396371): Disabled due to flakiness.
-IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
-                       DISABLED_NavigateToCrashedSingletonTab) {
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NavigateToCrashedSingletonTab) {
   const GURL singleton_url(GetContentSettingsURL());
   WebContents* web_contents = chrome::AddSelectedTabWithURL(
       browser(), singleton_url, ui::PAGE_TRANSITION_LINK);
@@ -1381,7 +1380,14 @@
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
 
   // Kill the singleton tab.
-  web_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
+    content::RenderFrameDeletedObserver crash_observer(
+        web_contents->GetMainFrame());
+    web_contents->GetMainFrame()->GetProcess()->Shutdown(1);
+    crash_observer.WaitUntilDeleted();
+  }
   EXPECT_TRUE(web_contents->IsCrashed());
 
   NavigateParams params(MakeNavigateParams());
diff --git a/chrome/browser/ui/browser_unittest.cc b/chrome/browser/ui/browser_unittest.cc
index 1b314e6..6f87e02 100644
--- a/chrome/browser/ui/browser_unittest.cc
+++ b/chrome/browser/ui/browser_unittest.cc
@@ -69,7 +69,8 @@
   EXPECT_FALSE(raw_contents2->IsLoading());
 
   // Simulate the second tab crashing.
-  raw_contents2->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  WebContentsTester::For(raw_contents2)
+      ->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
   EXPECT_TRUE(raw_contents2->IsCrashed());
 
   // Selecting the second tab does not cause a load or clear the crash.
@@ -125,7 +126,8 @@
   EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_PRINT));
   EXPECT_TRUE(chrome::CanPrint(browser()));
 
-  raw_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  WebContentsTester::For(raw_contents)
+      ->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
 
   EXPECT_TRUE(raw_contents->IsCrashed());
   EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_PRINT));
@@ -154,7 +156,8 @@
   EXPECT_TRUE(chrome::CanZoomIn(raw_contents));
   EXPECT_TRUE(chrome::CanZoomOut(raw_contents));
 
-  raw_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  WebContentsTester::For(raw_contents)
+      ->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
 
   EXPECT_TRUE(raw_contents->IsCrashed());
   EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_ZOOM_PLUS));
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index e1adfc11..307313f 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/download_shelf.h"
@@ -115,7 +116,7 @@
       apps::AppServiceProxyFactory::GetForProfileRedirectInIncognito(profile);
   proxy->LaunchAppWithUrl(web_app::kHelpAppId, ui::EventFlags::EF_NONE,
                           GURL("chrome://help-app/updates"), source,
-                          display::kDefaultDisplayId);
+                          apps::MakeWindowInfo(display::kDefaultDisplayId));
 }
 
 #endif
@@ -145,7 +146,7 @@
   apps::AppServiceProxy* proxy =
       apps::AppServiceProxyFactory::GetForProfileRedirectInIncognito(profile);
   proxy->Launch(web_app::kHelpAppId, ui::EventFlags::EF_NONE, app_launch_source,
-                display::kDefaultDisplayId);
+                apps::MakeWindowInfo(display::kDefaultDisplayId));
 #else
   GURL url;
   switch (source) {
diff --git a/chrome/browser/ui/extensions/extension_enable_flow.cc b/chrome/browser/ui/extensions/extension_enable_flow.cc
index 3a693485..882c82e 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow.cc
+++ b/chrome/browser/ui/extensions/extension_enable_flow.cc
@@ -14,8 +14,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 
@@ -198,20 +196,18 @@
 void ExtensionEnableFlow::StartObserving() {
   extension_registry_observer_.Add(
       extensions::ExtensionRegistry::Get(profile_));
-  registrar_.Add(this,
-                 extensions::NOTIFICATION_EXTENSION_LOAD_ERROR,
-                 content::Source<Profile>(profile_));
+  load_error_observation_.Observe(extensions::LoadErrorReporter::GetInstance());
 }
 
 void ExtensionEnableFlow::StopObserving() {
-  registrar_.RemoveAll();
   extension_registry_observer_.RemoveAll();
+  load_error_observation_.Reset();
 }
 
-void ExtensionEnableFlow::Observe(int type,
-                                  const content::NotificationSource& source,
-                                  const content::NotificationDetails& details) {
-  DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_LOAD_ERROR, type);
+void ExtensionEnableFlow::OnLoadFailure(
+    content::BrowserContext* browser_context,
+    const base::FilePath& file_path,
+    const std::string& error) {
   StopObserving();
   delegate_->ExtensionEnableFlowAborted(
       /*user_initiated=*/false);  // |delegate_| may delete us.
diff --git a/chrome/browser/ui/extensions/extension_enable_flow.h b/chrome/browser/ui/extensions/extension_enable_flow.h
index 165d3816..1478b9f1 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow.h
+++ b/chrome/browser/ui/extensions/extension_enable_flow.h
@@ -12,11 +12,11 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
+#include "chrome/browser/extensions/load_error_reporter.h"
 #include "chrome/common/buildflags.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 
@@ -37,7 +37,7 @@
 // extension is enabled already). Otherwise, a re-enable install prompt is
 // shown to user. The extension is enabled when user acknowledges it or the
 // flow is aborted when user declines it.
-class ExtensionEnableFlow : public content::NotificationObserver,
+class ExtensionEnableFlow : public extensions::LoadErrorReporter::Observer,
                             public extensions::ExtensionRegistryObserver {
  public:
   ExtensionEnableFlow(Profile* profile,
@@ -58,6 +58,11 @@
 
   const std::string& extension_id() const { return extension_id_; }
 
+  // LoadErrorReporter::Observer:
+  void OnLoadFailure(content::BrowserContext* browser_context,
+                     const base::FilePath& file_path,
+                     const std::string& error) override;
+
  private:
   // Runs the enable flow. It starts by checking if the extension is loaded.
   // If not, it tries to reload it. If the load is asynchronous, wait for the
@@ -89,11 +94,6 @@
   void StartObserving();
   void StopObserving();
 
-  // content::NotificationObserver overrides:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   // extensions::ExtensionRegistryObserver overrides:
   void OnExtensionLoaded(content::BrowserContext* browser_context,
                          const extensions::Extension* extension) override;
@@ -118,13 +118,16 @@
   gfx::NativeWindow parent_window_ = nullptr;
 
   std::unique_ptr<ExtensionInstallPrompt> prompt_;
-  content::NotificationRegistrar registrar_;
 
   // Listen to extension load notification.
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
       extension_registry_observer_{this};
 
+  base::ScopedObservation<extensions::LoadErrorReporter,
+                          extensions::LoadErrorReporter::Observer>
+      load_error_observation_{this};
+
   base::WeakPtrFactory<ExtensionEnableFlow> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionEnableFlow);
diff --git a/chrome/browser/ui/extensions/extension_installed_notification.cc b/chrome/browser/ui/extensions/extension_installed_notification.cc
index ea7f89df..7a414ff 100644
--- a/chrome/browser/ui/extensions/extension_installed_notification.cc
+++ b/chrome/browser/ui/extensions/extension_installed_notification.cc
@@ -22,7 +22,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/display/types/display_constants.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/message_center/public/cpp/notification.h"
 
@@ -81,6 +80,5 @@
       apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerNone,
                           WindowOpenDisposition::NEW_FOREGROUND_TAB,
                           true /* preferred_containner */),
-      apps::mojom::LaunchSource::kFromInstalledNotification,
-      display::kInvalidDisplayId);
+      apps::mojom::LaunchSource::kFromInstalledNotification);
 }
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index e42fba28..e098885 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -1258,7 +1258,8 @@
 
 base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
                                      const base::FilePath& cur_dir,
-                                     const base::CommandLine& command_line) {
+                                     const base::CommandLine& command_line,
+                                     bool ignore_profile_picker) {
 // If the browser is launched due to activation on Windows native notification,
 // the profile id encoded in the notification launch id should be chosen over
 // all others.
@@ -1285,7 +1286,8 @@
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  if (ShouldShowProfilePickerAtProcessLaunch(profile_manager, command_line)) {
+  if (!ignore_profile_picker &&
+      ShouldShowProfilePickerAtProcessLaunch(profile_manager, command_line)) {
     // Open the picker only if no URLs have been provided to launch Chrome. If
     // URLs are provided, open them in the last profile, instead.
     Profile* guest_profile =
@@ -1315,8 +1317,8 @@
                            const base::FilePath& cur_dir,
                            const base::CommandLine& command_line) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
-  base::FilePath profile_path =
-      GetStartupProfilePath(user_data_dir, cur_dir, command_line);
+  base::FilePath profile_path = GetStartupProfilePath(
+      user_data_dir, cur_dir, command_line, /*ignore_profile_picker=*/false);
   Profile* profile = profile_manager->GetProfile(profile_path);
 
   // If there is no entry in profile attributes storage, the profile is deleted,
diff --git a/chrome/browser/ui/startup/startup_browser_creator.h b/chrome/browser/ui/startup/startup_browser_creator.h
index 7f4b56f9..7fa3fc7c 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.h
+++ b/chrome/browser/ui/startup/startup_browser_creator.h
@@ -210,9 +210,15 @@
 
 // Returns the path that contains the profile that should be loaded on process
 // startup.
+// When the profile picker is shown on startup, this returns the Guest profile
+// path. On Mac, the startup profile path is also used to open URLs at startup,
+// bypassing the profile picker, because the profile picker does not support it.
+// TODO(https://crbug.com/1155158): Remove this parameter once the picker
+// supports opening URLs.
 base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
                                      const base::FilePath& cur_dir,
-                                     const base::CommandLine& command_line);
+                                     const base::CommandLine& command_line,
+                                     bool ignore_profile_picker);
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
 // Returns the profile that should be loaded on process startup. This is either
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 4a970a80..efdf507 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -2300,7 +2300,8 @@
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
       command_line, current_dir,
-      GetStartupProfilePath(user_data_dir, current_dir, command_line));
+      GetStartupProfilePath(user_data_dir, current_dir, command_line,
+                            /*ignore_profile_picker=*/false));
   base::RunLoop().RunUntilIdle();
 
   // The picker is shown again if no profile was previously opened.
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 7d7530d1..0dfe4e5 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -59,6 +59,7 @@
 // Enables tabs to scroll in the tabstrip. https://crbug.com/951078
 const base::Feature kScrollableTabStrip{"ScrollableTabStrip",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
+const char kMinimumTabWidthFeatureParameterName[] = "minTabWidth";
 
 // Enables buttons to permanently appear on the tabstrip when
 // scrollable-tabstrip is enabled. https://crbug.com/1116118
@@ -168,7 +169,7 @@
 // Enables a separate group of settings (speed, button swap, and acceleration)
 // for pointing sticks (such as TrackPoints).
 const base::Feature kSeparatePointingStickSettings{
-    "SeparatePointingStickSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SeparatePointingStickSettings", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace features
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 044ec21..37b4dde 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -44,6 +44,7 @@
 extern const base::Feature kProminentDarkModeActiveTabTitle;
 
 extern const base::Feature kScrollableTabStrip;
+extern const char kMinimumTabWidthFeatureParameterName[];
 
 extern const base::Feature kScrollableTabStripButtons;
 
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index bb9107a..c036aa5 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -243,12 +243,13 @@
 #endif
 
 constexpr int kDebugModifier =
-    ui::EF_PLATFORM_ACCELERATOR | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN;
+    ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN;
 
 // Accelerators to enable if features::UIDebugTools is true.
 constexpr AcceleratorMapping kUIDebugAcceleratorMap[] = {
     {ui::VKEY_T, kDebugModifier, IDC_DEBUG_TOGGLE_TABLET_MODE},
     {ui::VKEY_V, kDebugModifier, IDC_DEBUG_PRINT_VIEW_TREE},
+    {ui::VKEY_M, kDebugModifier, IDC_DEBUG_PRINT_VIEW_TREE_DETAILS},
 };
 
 const int kRepeatableCommandIds[] = {
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
index 518060a..2d14be0 100644
--- a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
@@ -25,7 +25,6 @@
 #include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_app_instance.h"
 #include "content/public/test/browser_test.h"
-#include "ui/display/types/display_constants.h"
 
 class AppDialogViewBrowserTest : public DialogBrowserTest {
  public:
@@ -109,9 +108,9 @@
       app_instance_->SendRefreshAppList(
           std::vector<arc::mojom::AppInfo>(1, app));
       app_service_proxy_->FlushMojoCallsForTesting();
-      app_service_proxy_->Launch(app_id_, ui::EventFlags::EF_NONE,
-                                 apps::mojom::LaunchSource::kFromChromeInternal,
-                                 display::kInvalidDisplayId);
+      app_service_proxy_->Launch(
+          app_id_, ui::EventFlags::EF_NONE,
+          apps::mojom::LaunchSource::kFromChromeInternal);
     } else {
       std::map<std::string, apps::PauseData> pause_data;
       pause_data[app_id_].hours = 3;
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_view.cc b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
index 1cd6cac5..19a46146 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_view.cc
+++ b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
@@ -17,6 +17,14 @@
       controller_(controller) {
   DCHECK(base::FeatureList::IsEnabled(
       features::kAutofillAddressProfileSavePrompt));
+  SetAcceptCallback(base::BindOnce(
+      &SaveAddressProfileBubbleController::OnUserDecision,
+      base::Unretained(controller_),
+      AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted));
+  SetCancelCallback(base::BindOnce(
+      &SaveAddressProfileBubbleController::OnUserDecision,
+      base::Unretained(controller_),
+      AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined));
 }
 
 bool SaveAddressProfileView::ShouldShowCloseButton() const {
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_view.h b/chrome/browser/ui/views/autofill/save_address_profile_view.h
index 9aae948..842d90ea 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_view.h
+++ b/chrome/browser/ui/views/autofill/save_address_profile_view.h
@@ -28,6 +28,9 @@
                          content::WebContents* web_contents,
                          SaveAddressProfileBubbleController* controller);
 
+  SaveAddressProfileView(const SaveAddressProfileView&) = delete;
+  SaveAddressProfileView& operator=(const SaveAddressProfileView&) = delete;
+
   // views::WidgetDelegate:
   bool ShouldShowCloseButton() const override;
   base::string16 GetWindowTitle() const override;
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_view_unittest.cc b/chrome/browser/ui/views/autofill/save_address_profile_view_unittest.cc
index fd6c888b..25089c5 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_view_unittest.cc
+++ b/chrome/browser/ui/views/autofill/save_address_profile_view_unittest.cc
@@ -5,14 +5,28 @@
 #include "chrome/browser/ui/views/autofill/save_address_profile_view.h"
 
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/autofill/save_address_profile_bubble_controller.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
 
+class MockSaveAddressProfileBubbleController
+    : public SaveAddressProfileBubbleController {
+ public:
+  MOCK_METHOD(base::string16, GetWindowTitle, (), (const, override));
+  MOCK_METHOD(void,
+              OnUserDecision,
+              (AutofillClient::SaveAddressProfileOfferUserDecision decision),
+              (override));
+  MOCK_METHOD(void, OnBubbleClosed, (), (override));
+};
+
 class SaveAddressProfileViewTest : public ChromeViewsTestBase {
  public:
   SaveAddressProfileViewTest();
@@ -29,6 +43,9 @@
   }
 
   SaveAddressProfileView* view() { return view_; }
+  MockSaveAddressProfileBubbleController* mock_controller() {
+    return &mock_controller_;
+  }
 
  private:
   base::test::ScopedFeatureList feature_list_;
@@ -38,6 +55,7 @@
   std::unique_ptr<content::WebContents> test_web_contents_;
   std::unique_ptr<views::Widget> anchor_widget_;
   SaveAddressProfileView* view_;
+  testing::NiceMock<MockSaveAddressProfileBubbleController> mock_controller_;
 };
 
 SaveAddressProfileViewTest::SaveAddressProfileViewTest() {
@@ -49,6 +67,9 @@
 }
 
 void SaveAddressProfileViewTest::CreateViewAndShow() {
+  ON_CALL(*mock_controller(), GetWindowTitle())
+      .WillByDefault(testing::Return(base::string16()));
+
   // The bubble needs the parent as an anchor.
   views::Widget::InitParams params =
       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
@@ -58,9 +79,9 @@
   anchor_widget_->Init(std::move(params));
   anchor_widget_->Show();
 
-  view_ = new SaveAddressProfileView(anchor_widget_->GetContentsView(),
-                                     test_web_contents_.get(),
-                                     /*controller=*/nullptr);
+  view_ =
+      new SaveAddressProfileView(anchor_widget_->GetContentsView(),
+                                 test_web_contents_.get(), mock_controller());
   views::BubbleDialogDelegateView::CreateBubble(view_)->Show();
 }
 
@@ -69,4 +90,22 @@
   EXPECT_TRUE(view()->ShouldShowCloseButton());
 }
 
+TEST_F(SaveAddressProfileViewTest, AcceptInvokesTheController) {
+  CreateViewAndShow();
+  EXPECT_CALL(
+      *mock_controller(),
+      OnUserDecision(
+          AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted));
+  view()->AcceptDialog();
+}
+
+TEST_F(SaveAddressProfileViewTest, CancelInvokesTheController) {
+  CreateViewAndShow();
+  EXPECT_CALL(
+      *mock_controller(),
+      OnUserDecision(
+          AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined));
+  view()->CancelDialog();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/browser_commands_views.cc b/chrome/browser/ui/views/browser_commands_views.cc
index fe9d1fa..be720cd 100644
--- a/chrome/browser/ui/views/browser_commands_views.cc
+++ b/chrome/browser/ui/views/browser_commands_views.cc
@@ -9,7 +9,38 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "ui/base/ui_base_features.h"
-#include "ui/views/debug_utils.h"
+#include "ui/views/view.h"
+#include "ui/views/view_utils.h"
+#include "ui/views/widget/widget.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#include "ui/wm/public/activation_client.h"
+#endif
+
+#if defined(OS_MAC)
+#include "chrome/browser/platform_util.h"
+#endif
+
+namespace {
+views::View* GetActiveWindowRootView(const Browser* browser) {
+#if defined(USE_AURA)
+  wm::ActivationClient* client = wm::GetActivationClient(
+      browser->window()->GetNativeWindow()->GetRootWindow());
+  if (!client)
+    return nullptr;
+  gfx::NativeWindow active_window = client->GetActiveWindow();
+#elif defined(OS_MAC)
+  NSWindow* active_window = platform_util::GetActiveWindow();
+  if (!active_window)
+    return nullptr;
+#endif
+
+  views::Widget* widget =
+      views::Widget::GetWidgetForNativeWindow(active_window);
+  return widget ? widget->GetRootView() : nullptr;
+}
+}  // namespace
 
 namespace chrome {
 
@@ -33,11 +64,13 @@
       break;
     }
     case IDC_DEBUG_PRINT_VIEW_TREE:
-      // TODO(weili): replace with a new tree dumping utility which can show
-      // detailed property info.
-      PrintViewHierarchy(BrowserView::GetBrowserViewForBrowser(browser));
+      if (views::View* view = GetActiveWindowRootView(browser))
+        PrintViewHierarchy(view);
       break;
-
+    case IDC_DEBUG_PRINT_VIEW_TREE_DETAILS:
+      if (views::View* view = GetActiveWindowRootView(browser))
+        PrintViewHierarchy(view, /* verbose= */ true);
+      break;
     default:
       NOTREACHED() << "Unimplemented UI Debug command: " << id;
       break;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 5a4c76cb..d569861d 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -156,6 +156,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/password_protection/metrics_util.h"
 #include "components/sessions/core/tab_restore_service.h"
+#include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/version_info/channel.h"
@@ -2955,11 +2956,10 @@
 
 void BrowserView::PaintChildren(const views::PaintInfo& paint_info) {
   views::ClientView::PaintChildren(paint_info);
-  // Don't reset the instance before it had a chance to get compositor callback.
-  if (!histogram_helper_) {
-    histogram_helper_ = BrowserWindowHistogramHelper::
-        MaybeRecordValueAndCreateInstanceOnBrowserPaint(
-            GetWidget()->GetCompositor());
+  static bool did_first_paint = false;
+  if (!did_first_paint) {
+    did_first_paint = true;
+    startup_metric_utils::RecordBrowserWindowFirstPaint(base::TimeTicks::Now());
   }
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 9ba858b8..e4955cc 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -22,7 +22,6 @@
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/extensions/extension_commands_global_registry.h"
 #include "chrome/browser/extensions/extension_keybinding_registry.h"
-#include "chrome/browser/metrics/browser_window_histogram_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
@@ -937,8 +936,6 @@
   std::unique_ptr<ExtensionKeybindingRegistryViews>
       extension_keybinding_registry_;
 
-  std::unique_ptr<BrowserWindowHistogramHelper> histogram_helper_;
-
   std::unique_ptr<FullscreenControlHost> fullscreen_control_host_;
 
   // If the Window Placement experiment is enabled and fullscreen is requested
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index 4a4671a..29b6525 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -39,6 +39,7 @@
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_utils.h"
 #include "extensions/common/extension.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -397,7 +398,13 @@
   ASSERT_TRUE(app_banner_manager_->WaitForInstallableCheck());
   EXPECT_TRUE(pwa_install_view_->GetVisible());
 
-  web_contents_->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    content::RenderFrameDeletedObserver crash_observer(
+        web_contents_->GetMainFrame());
+    web_contents_->GetMainFrame()->GetProcess()->Shutdown(1);
+    crash_observer.WaitUntilDeleted();
+  }
   ASSERT_TRUE(web_contents_->IsCrashed());
   PwaInstallIconChangeWaiter::VerifyIconVisibility(pwa_install_view_, false);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 9e9c088..e2c0105 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -1387,7 +1387,8 @@
 
 }  // namespace
 
-#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+// Flaky. https://crbug.com/1176998
+#if (defined(OS_MAC) && defined(ARCH_CPU_ARM64)) || defined(OS_LINUX)
 // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
 #define MAYBE_DragToSeparateWindow DISABLED_DragToSeparateWindow
 #else
@@ -2059,8 +2060,8 @@
 
 }  // namespace
 
-// Flaky. http://crbug.com/1128774
-#if defined(OS_MAC) || defined(OS_WIN)
+// Flaky. http://crbug.com/1128774 and http://crbug.com/1176998
+#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX)
 // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
 // These were flaking on all macs, so commented out ARCH_ above for
 // crbug.com/1160917 too.
@@ -2696,7 +2697,8 @@
             browser2->window()->GetBounds().ToString());
 }
 
-#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+// Flaky. https://crbug.com/1176998
+#if (defined(OS_MAC) && defined(ARCH_CPU_ARM64)) || defined(OS_LINUX)
 // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
 #define MAYBE_DragSingleTabToSeparateWindow \
   DISABLED_DragSingleTabToSeparateWindow
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 70a78509..052a824 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -988,19 +988,33 @@
 
 // static
 int TabStyleViews::GetMinimumActiveWidth() {
-  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip))
-    return 72;
-  return TabCloseButton::GetGlyphSize() + GetContentsHorizontalInsetSize() * 2;
+  int min_active_width =
+      TabCloseButton::GetGlyphSize() + GetContentsHorizontalInsetSize() * 2;
+  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
+    return std::max(min_active_width,
+                    base::GetFieldTrialParamByFeatureAsInt(
+                        features::kScrollableTabStrip,
+                        features::kMinimumTabWidthFeatureParameterName, 72));
+  }
+  return min_active_width;
 }
 
 // static
 int TabStyleViews::GetMinimumInactiveWidth() {
-  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip))
-    return 72;
   // Allow tabs to shrink until they appear to be 16 DIP wide excluding
   // outer corners.
   constexpr int kInteriorWidth = 16;
   // The overlap contains the trailing separator that is part of the interior
   // width; avoid double-counting it.
-  return kInteriorWidth - GetSeparatorSize().width() + GetTabOverlap();
+  int min_inactive_width =
+      kInteriorWidth - GetSeparatorSize().width() + GetTabOverlap();
+
+  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
+    return std::max(min_inactive_width,
+                    base::GetFieldTrialParamByFeatureAsInt(
+                        features::kScrollableTabStrip,
+                        features::kMinimumTabWidthFeatureParameterName, 72));
+  }
+
+  return min_inactive_width;
 }
diff --git a/chrome/browser/ui/views/window_name_prompt.cc b/chrome/browser/ui/views/window_name_prompt.cc
index 02cf79c..0ec2d64 100644
--- a/chrome/browser/ui/views/window_name_prompt.cc
+++ b/chrome/browser/ui/views/window_name_prompt.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -20,8 +21,13 @@
 
 void SetBrowserTitleFromTextfield(Browser* browser,
                                   ui::DialogModel* dialog_model) {
-  browser->SetWindowUserTitle(base::UTF16ToUTF8(
-      dialog_model->GetTextfieldByUniqueId(kWindowNameFieldId)->text()));
+  std::string text = base::UTF16ToUTF8(
+      dialog_model->GetTextfieldByUniqueId(kWindowNameFieldId)->text());
+  if (text.empty())
+    base::RecordAction(base::UserMetricsAction("WindowNaming_Cleared"));
+  else
+    base::RecordAction(base::UserMetricsAction("WindowNaming_Set"));
+  browser->SetWindowUserTitle(text);
 }
 
 std::unique_ptr<views::DialogDelegate> CreateWindowNamePrompt(
@@ -50,6 +56,8 @@
 void DoShowWindowNamePrompt(Browser* browser,
                             gfx::NativeView anchor,
                             gfx::NativeWindow context) {
+  base::RecordAction(base::UserMetricsAction("WindowNaming_DialogShown"));
+
   auto prompt = CreateWindowNamePrompt(browser);
   prompt->SetOwnedByWidget(true);
   views::DialogDelegate::CreateDialogWidget(std::move(prompt), context, anchor)
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index 4d1ae0c..d737f75 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/web_launch/web_launch_files_helper.h"
 #include "chrome/common/webui_url_constants.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/display/types/display_constants.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
@@ -205,15 +204,13 @@
   auto event_flags = apps::GetEventFlags(
       apps::mojom::LaunchContainer::kLaunchContainerNone,
       WindowOpenDisposition::NEW_WINDOW, /* prefer_container */ false);
-  auto display_id = display::kInvalidDisplayId;
 
   if (params.url.is_empty()) {
-    app_service->Launch(app_id.value(), event_flags, params.launch_source,
-                        display_id);
+    app_service->Launch(app_id.value(), event_flags, params.launch_source);
   } else {
     DCHECK(params.url.is_valid());
     app_service->LaunchAppWithUrl(app_id.value(), event_flags, params.url,
-                                  params.launch_source, display_id);
+                                  params.launch_source);
   }
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 1849ae70..c379a2bb 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/devtools/protocol/browser_handler.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
@@ -776,7 +777,14 @@
       NavigateAndAwaitInstallabilityCheck(browser(), GetInstallableAppURL()));
   content::WebContents* tab_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  tab_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    content::RenderFrameDeletedObserver crash_observer(
+        tab_contents->GetMainFrame());
+    tab_contents->GetMainFrame()->GetProcess()->Shutdown(1);
+    crash_observer.WaitUntilDeleted();
+  }
   ASSERT_TRUE(tab_contents->IsCrashed());
 
   EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kDisabled);
@@ -968,26 +976,6 @@
       "navigator.geolocation.getCurrentPosition(function(){});"));
 }
 
-// Check that no web app is launched during shutdown.
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, Shutdown) {
-  const GURL app_url = GetSecureAppURL();
-  const AppId app_id = InstallPWA(app_url);
-  apps::AppLaunchParams params(
-      app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceTest);
-
-  BrowserHandler handler(nullptr, std::string());
-  handler.Close();
-  ui_test_utils::WaitForBrowserToClose();
-
-  content::WebContents* const web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile())
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParams(std::move(params));
-  EXPECT_EQ(web_contents, nullptr);
-}
-
 // Ensure that web app windows with blank titles don't display the URL as a
 // default window title.
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, EmptyTitlesDoNotDisplayUrl) {
@@ -1271,4 +1259,37 @@
   EXPECT_EQ(nullptr, app_browser->GetStatusBubbleForTesting());
 }
 
+class WebAppBrowserTest_NoDestroyProfile : public WebAppBrowserTest {
+ public:
+  WebAppBrowserTest_NoDestroyProfile() {
+    // This test only makes sense when DestroyProfileOnBrowserClose is
+    // disabled.
+    feature_list_.InitAndDisableFeature(
+        features::kDestroyProfileOnBrowserClose);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Check that no web app is launched during shutdown.
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_NoDestroyProfile, Shutdown) {
+  const GURL app_url = GetSecureAppURL();
+  const AppId app_id = InstallPWA(app_url);
+  apps::AppLaunchParams params(
+      app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+      WindowOpenDisposition::NEW_WINDOW,
+      apps::mojom::AppLaunchSource::kSourceTest);
+
+  BrowserHandler handler(nullptr, std::string());
+  handler.Close();
+  ui_test_utils::WaitForBrowserToClose();
+
+  content::WebContents* const web_contents =
+      apps::AppServiceProxyFactory::GetForProfile(profile())
+          ->BrowserAppLauncher()
+          ->LaunchAppWithParams(std::move(params));
+  EXPECT_EQ(web_contents, nullptr);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 0759493..70f454a 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -302,6 +302,7 @@
     "chrome://crostini-installer",
     "chrome://cryptohome",
     "chrome://drive-internals",
+    "chrome://family-link-user-internals",
     "chrome://help-app",
     "chrome://internet-config-dialog",
     "chrome://internet-detail-dialog",
@@ -318,7 +319,6 @@
     "chrome://slow",
     "chrome://smb-credentials-dialog",
     "chrome://smb-share-dialog",
-    "chrome://supervised-user-internals",
     "chrome://sys-internals",
     "chrome-untrusted://terminal",
 #endif
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 937c5f8..542424d4 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -297,7 +297,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
+#include "chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.h"
 #endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -405,9 +405,9 @@
 }
 
 template <>
-WebUIController* NewWebUI<chromeos::DiagnosticsUI>(WebUI* web_ui,
-                                                   const GURL& url) {
-  return new chromeos::DiagnosticsUI(
+WebUIController* NewWebUI<chromeos::DiagnosticsDialogUI>(WebUI* web_ui,
+                                                         const GURL& url) {
+  return new chromeos::DiagnosticsDialogUI(
       web_ui, base::BindRepeating(&CreateChromeSelectFilePolicy));
 }
 
@@ -740,7 +740,7 @@
     return &NewWebUI<chromeos::PowerUI>;
   if (base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp) &&
       url.host_piece() == chromeos::kChromeUIDiagnosticsAppHost) {
-    return &NewWebUI<chromeos::DiagnosticsUI>;
+    return &NewWebUI<chromeos::DiagnosticsDialogUI>;
   }
   if (url.host_piece() == chromeos::kChromeUIPrintManagementHost)
     return &NewWebUI<chromeos::printing::printing_manager::PrintManagementUI>;
@@ -989,8 +989,8 @@
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  if (url.host_piece() == chrome::kChromeUISupervisedUserInternalsHost)
-    return &NewWebUI<SupervisedUserInternalsUI>;
+  if (url.host_piece() == chrome::kChromeUIFamilyLinkUserInternalsHost)
+    return &NewWebUI<FamilyLinkUserInternalsUI>;
 #endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ui/webui/chromeos/diagnostics_dialog.cc b/chrome/browser/ui/webui/chromeos/diagnostics_dialog.cc
new file mode 100644
index 0000000..7978728f
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/diagnostics_dialog.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 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/chromeos/diagnostics_dialog.h"
+
+#include <string>
+
+#include "chromeos/components/diagnostics_ui/url_constants.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+
+namespace chromeos {
+
+// Scale factor for size of the diagnostics dialog, based on display size.
+const float kDiagnosticsDialogScale = .8;
+
+// static
+void DiagnosticsDialog::ShowDialog() {
+  DiagnosticsDialog* dialog = new DiagnosticsDialog();
+  dialog->ShowSystemDialog();
+}
+
+DiagnosticsDialog::DiagnosticsDialog()
+    : SystemWebDialogDelegate(GURL(kChromeUIDiagnosticsAppUrl),
+                              /*title=*/base::string16()) {}
+
+DiagnosticsDialog::~DiagnosticsDialog() = default;
+
+const std::string& DiagnosticsDialog::Id() {
+  return id_;
+}
+
+void DiagnosticsDialog::GetDialogSize(gfx::Size* size) const {
+  const display::Display display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+
+  gfx::Size display_size = display.size();
+
+  if (display.rotation() == display::Display::ROTATE_90 ||
+      display.rotation() == display::Display::ROTATE_270) {
+    display_size = gfx::Size(display_size.height(), display_size.width());
+  }
+
+  display_size = gfx::Size(display_size.width() * kDiagnosticsDialogScale,
+                           display_size.height() * kDiagnosticsDialogScale);
+
+  *size = display_size;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/diagnostics_dialog.h b/chrome/browser/ui/webui/chromeos/diagnostics_dialog.h
new file mode 100644
index 0000000..5a3aad6b
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/diagnostics_dialog.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2020 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_CHROMEOS_DIAGNOSTICS_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_DIAGNOSTICS_DIALOG_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
+
+namespace chromeos {
+
+class DiagnosticsDialog : public SystemWebDialogDelegate {
+ public:
+  static void ShowDialog();
+
+ protected:
+  DiagnosticsDialog();
+  ~DiagnosticsDialog() override;
+
+  DiagnosticsDialog(const DiagnosticsDialog&) = delete;
+  DiagnosticsDialog& operator=(const DiagnosticsDialog&) = delete;
+
+  // SystemWebDialogDelegate
+  const std::string& Id() override;
+
+  // ui::WebDialogDelegate
+  void GetDialogSize(gfx::Size* size) const override;
+
+ private:
+  const std::string id_ = "diagnostics-dialog";
+};
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_DIAGNOSTICS_DIALOG_H_
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
index db99acd..1756539d 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
@@ -12,25 +12,39 @@
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/common/url_constants.h"
 
+#include "ui/aura/window.h"
 #include "ui/base/ime/chromeos/ime_bridge.h"
 
 namespace chromeos {
 
+constexpr gfx::Size kDefaultWindowSize(340, 390);
+
 ui::TextInputClient* EmojiPickerDialog::input_client = nullptr;
 gfx::Range EmojiPickerDialog::selection_range = gfx::Range();
 
 EmojiPickerDialog::EmojiPickerDialog() {
-  ui::InputMethod* input_method =
-      ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
-  input_client = input_method->GetTextInputClient();
-  input_client->GetEditableSelectionRange(&selection_range);
-
   set_can_resize(false);
 }
 
 void EmojiPickerDialog::Show() {
-  chrome::ShowWebDialog(nullptr, ProfileManager::GetActiveUserProfile(),
-                        new EmojiPickerDialog());
+  ui::InputMethod* input_method =
+      ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
+  if (input_method) {
+    input_client = input_method->GetTextInputClient();
+  }
+  if (input_client) {
+    input_client->GetEditableSelectionRange(&selection_range);
+  }
+  const auto caret_bounds =
+      input_client ? input_client->GetCaretBounds() : gfx::Rect();
+  gfx::NativeWindow window = chrome::ShowWebDialog(
+      nullptr, ProfileManager::GetActiveUserProfile(), new EmojiPickerDialog());
+  // For now, this can overflow the screen.
+  if (input_client) {
+    window->SetBounds(gfx::Rect(caret_bounds.x(), caret_bounds.bottom(),
+                                kDefaultWindowSize.width(),
+                                kDefaultWindowSize.height()));
+  }
 }
 
 ui::ModalType EmojiPickerDialog::GetDialogModalType() const {
@@ -49,9 +63,7 @@
     std::vector<content::WebUIMessageHandler*>* handlers) const {}
 
 void EmojiPickerDialog::GetDialogSize(gfx::Size* size) const {
-  const int kDefaultWidth = 340;
-  const int kDefaultHeight = 390;
-  size->SetSize(kDefaultWidth, kDefaultHeight);
+  *size = kDefaultWindowSize;
 }
 
 std::string EmojiPickerDialog::GetDialogArgs() const {
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
index 182656e..9cfbc6d7 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -12,8 +12,8 @@
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/system/fake_input_device_settings.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/ash/system/fake_input_device_settings.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chromeos/dbus/audio/fake_cras_audio_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
index 4d99b15..e30f5b61 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ash/system/pointer_device_observer.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "device/bluetooth/bluetooth_adapter.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 6faa3c4..da8302f1 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -18,6 +18,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/branding_buildflags.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/configuration_keys.h"
@@ -29,7 +30,6 @@
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index aa5ac9d..4a99cd4 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -23,6 +23,7 @@
 #include "base/system/sys_info.h"
 #include "base/values.h"
 #include "build/branding_buildflags.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h"
@@ -35,7 +36,6 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/enrollment_requisition_manager.h"
 #include "chrome/browser/chromeos/settings/shutdown_policy_handler.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 3fcc442..f8dcd561 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -28,6 +28,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -52,7 +53,6 @@
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/system_clock.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_metrics.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
index f152655..ae7e2ea 100644
--- a/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
@@ -17,12 +17,12 @@
 #include "base/values.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/screens/welcome_screen.h"
 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
diff --git a/chrome/browser/ui/webui/chromeos/set_time_ui.cc b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
index c638d99..4f84d47 100644
--- a/chrome/browser/ui/webui/chromeos/set_time_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
@@ -16,10 +16,10 @@
 #include "base/macros.h"
 #include "base/scoped_observation.h"
 #include "base/values.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service.h"
 #include "chrome/browser/chromeos/set_time_dialog.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
similarity index 80%
rename from chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
rename to chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
index 6429fa4e..2f71cd82 100644
--- a/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/supervised_user_internals_message_handler.h"
+#include "chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h"
 
 #include <memory>
 #include <utility>
@@ -35,7 +35,7 @@
 
 namespace {
 
-// Creates a 'section' for display on about:supervised-user-internals,
+// Creates a 'section' for display on about:family-link-user-internals,
 // consisting of a title and a list of fields. Returns a pointer to the new
 // section's contents, for use with |AddSectionEntry| below. Note that
 // |parent_list|, not the caller, owns the newly added section.
@@ -89,7 +89,8 @@
 }
 
 std::string FilteringBehaviorToString(
-    SupervisedUserURLFilter::FilteringBehavior behavior, bool uncertain) {
+    SupervisedUserURLFilter::FilteringBehavior behavior,
+    bool uncertain) {
   std::string result = FilteringBehaviorToString(behavior);
   if (uncertain)
     result += " (Uncertain)";
@@ -118,63 +119,70 @@
 
 }  // namespace
 
-SupervisedUserInternalsMessageHandler::SupervisedUserInternalsMessageHandler() =
+FamilyLinkUserInternalsMessageHandler::FamilyLinkUserInternalsMessageHandler() =
     default;
 
-SupervisedUserInternalsMessageHandler::
-    ~SupervisedUserInternalsMessageHandler() = default;
+FamilyLinkUserInternalsMessageHandler::
+    ~FamilyLinkUserInternalsMessageHandler() = default;
 
-void SupervisedUserInternalsMessageHandler::RegisterMessages() {
+void FamilyLinkUserInternalsMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   web_ui()->RegisterMessageCallback(
       "registerForEvents",
       base::BindRepeating(
-          &SupervisedUserInternalsMessageHandler::HandleRegisterForEvents,
+          &FamilyLinkUserInternalsMessageHandler::HandleRegisterForEvents,
           base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
       "getBasicInfo",
       base::BindRepeating(
-          &SupervisedUserInternalsMessageHandler::HandleGetBasicInfo,
+          &FamilyLinkUserInternalsMessageHandler::HandleGetBasicInfo,
           base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
       "tryURL",
-      base::BindRepeating(&SupervisedUserInternalsMessageHandler::HandleTryURL,
+      base::BindRepeating(&FamilyLinkUserInternalsMessageHandler::HandleTryURL,
                           base::Unretained(this)));
 }
 
-void SupervisedUserInternalsMessageHandler::OnURLFilterChanged() {
+void FamilyLinkUserInternalsMessageHandler::OnJavascriptDisallowed() {
+  scoped_observation_.Reset();
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+void FamilyLinkUserInternalsMessageHandler::OnURLFilterChanged() {
   SendBasicInfo();
 }
 
 SupervisedUserService*
-SupervisedUserInternalsMessageHandler::GetSupervisedUserService() {
+FamilyLinkUserInternalsMessageHandler::GetSupervisedUserService() {
   Profile* profile = Profile::FromWebUI(web_ui());
   return SupervisedUserServiceFactory::GetForProfile(
       profile->GetOriginalProfile());
 }
 
-void SupervisedUserInternalsMessageHandler::HandleRegisterForEvents(
+void FamilyLinkUserInternalsMessageHandler::HandleRegisterForEvents(
     const base::ListValue* args) {
   DCHECK(args->empty());
+  AllowJavascript();
   if (scoped_observation_.IsObserving())
     return;
 
   scoped_observation_.Observe(GetSupervisedUserService()->GetURLFilter());
 }
 
-void SupervisedUserInternalsMessageHandler::HandleGetBasicInfo(
+void FamilyLinkUserInternalsMessageHandler::HandleGetBasicInfo(
     const base::ListValue* args) {
   SendBasicInfo();
 }
 
-void SupervisedUserInternalsMessageHandler::HandleTryURL(
+void FamilyLinkUserInternalsMessageHandler::HandleTryURL(
     const base::ListValue* args) {
-  DCHECK_EQ(1u, args->GetSize());
+  DCHECK_EQ(2u, args->GetSize());
+  std::string callback_id;
   std::string url_str;
-  if (!args->GetString(0, &url_str))
+  if (!args->GetString(0, &callback_id) || !args->GetString(1, &url_str))
     return;
 
   GURL url = url_formatter::FixupURL(url_str, std::string());
@@ -195,12 +203,12 @@
       filter->GetMatchingAllowlistTitles(url);
   filter->GetFilteringBehaviorForURLWithAsyncChecks(
       url,
-      base::BindOnce(&SupervisedUserInternalsMessageHandler::OnTryURLResult,
-                     weak_factory_.GetWeakPtr(), allowlists),
+      base::BindOnce(&FamilyLinkUserInternalsMessageHandler::OnTryURLResult,
+                     weak_factory_.GetWeakPtr(), allowlists, callback_id),
       skip_manual_parent_filter);
 }
 
-void SupervisedUserInternalsMessageHandler::SendBasicInfo() {
+void FamilyLinkUserInternalsMessageHandler::SendBasicInfo() {
   std::unique_ptr<base::ListValue> section_list(new base::ListValue);
 
   base::ListValue* section_general = AddSection(section_list.get(), "General");
@@ -219,9 +227,9 @@
   AddSectionEntry(section_filter, "Denylist active", filter->HasDenylist());
   AddSectionEntry(section_filter, "Online checks active",
                   filter->HasAsyncURLChecker());
-  AddSectionEntry(section_filter, "Default behavior",
-                  FilteringBehaviorToString(
-                      filter->GetDefaultFilteringBehavior()));
+  AddSectionEntry(
+      section_filter, "Default behavior",
+      FilteringBehaviorToString(filter->GetDefaultFilteringBehavior()));
 
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
@@ -230,8 +238,8 @@
     for (const auto& account :
          identity_manager
              ->GetExtendedAccountInfoForAccountsWithRefreshToken()) {
-      base::ListValue* section_user = AddSection(section_list.get(),
-          "User Information for " + account.full_name);
+      base::ListValue* section_user = AddSection(
+          section_list.get(), "User Information for " + account.full_name);
       AddSectionEntry(section_user, "Account id",
                       account.account_id.ToString());
       AddSectionEntry(section_user, "Gaia", account.gaia);
@@ -246,27 +254,27 @@
 
   base::DictionaryValue result;
   result.Set("sections", std::move(section_list));
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "chrome.supervised_user_internals.receiveBasicInfo", result);
+  FireWebUIListener("basic-info-received", result);
 
   // Trigger retrieval of the user settings
   SupervisedUserSettingsService* settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(profile->GetProfileKey());
   user_settings_subscription_ =
       settings_service->SubscribeForSettingsChange(base::BindRepeating(
-          &SupervisedUserInternalsMessageHandler::SendSupervisedUserSettings,
+          &FamilyLinkUserInternalsMessageHandler::SendFamilyLinkUserSettings,
           weak_factory_.GetWeakPtr()));
 }
 
-void SupervisedUserInternalsMessageHandler::SendSupervisedUserSettings(
+void FamilyLinkUserInternalsMessageHandler::SendFamilyLinkUserSettings(
     const base::DictionaryValue* settings) {
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "chrome.supervised_user_internals.receiveUserSettings",
+  FireWebUIListener(
+      "user-settings-received",
       *(settings ? settings : std::make_unique<base::Value>().get()));
 }
 
-void SupervisedUserInternalsMessageHandler::OnTryURLResult(
+void FamilyLinkUserInternalsMessageHandler::OnTryURLResult(
     const std::map<std::string, base::string16>& allowlists,
+    const std::string& callback_id,
     SupervisedUserURLFilter::FilteringBehavior behavior,
     supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
@@ -283,13 +291,12 @@
   result.SetBoolean("manual", reason == supervised_user_error_page::MANUAL &&
                                   behavior == SupervisedUserURLFilter::ALLOW);
   result.SetString("allowlists", allowlists_str);
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "chrome.supervised_user_internals.receiveTryURLResult", result);
+  ResolveJavascriptCallback(base::Value(callback_id), result);
 }
 
-void SupervisedUserInternalsMessageHandler::OnSiteListUpdated() {}
+void FamilyLinkUserInternalsMessageHandler::OnSiteListUpdated() {}
 
-void SupervisedUserInternalsMessageHandler::OnURLChecked(
+void FamilyLinkUserInternalsMessageHandler::OnURLChecked(
     const GURL& url,
     SupervisedUserURLFilter::FilteringBehavior behavior,
     supervised_user_error_page::FilteringBehaviorReason reason,
@@ -299,6 +306,5 @@
   result.SetString("url", url.possibly_invalid_spec());
   result.SetString("result", FilteringBehaviorToString(behavior, uncertain));
   result.SetString("reason", FilteringBehaviorReasonToString(reason));
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "chrome.supervised_user_internals.receiveFilteringResult", result);
+  FireWebUIListener("filtering-result-received", result);
 }
diff --git a/chrome/browser/ui/webui/supervised_user_internals_message_handler.h b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
similarity index 71%
rename from chrome/browser/ui/webui/supervised_user_internals_message_handler.h
rename to chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
index 40a96712..b21f6c8 100644
--- a/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_MESSAGE_HANDLER_H_
 
 #include "base/callback_list.h"
 #include "base/macros.h"
@@ -19,18 +19,19 @@
 class ListValue;
 }  // namespace base
 
-// The implementation for the chrome://supervised-user-internals page.
-class SupervisedUserInternalsMessageHandler
+// The implementation for the chrome://family-link-user-internals page.
+class FamilyLinkUserInternalsMessageHandler
     : public content::WebUIMessageHandler,
       public SupervisedUserServiceObserver,
       public SupervisedUserURLFilter::Observer {
  public:
-  SupervisedUserInternalsMessageHandler();
-  ~SupervisedUserInternalsMessageHandler() override;
+  FamilyLinkUserInternalsMessageHandler();
+  ~FamilyLinkUserInternalsMessageHandler() override;
 
  private:
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
+  void OnJavascriptDisallowed() override;
 
   // SupervisedUserServiceObserver:
   void OnURLFilterChanged() override;
@@ -42,10 +43,11 @@
   void HandleTryURL(const base::ListValue* args);
 
   void SendBasicInfo();
-  void SendSupervisedUserSettings(const base::DictionaryValue* settings);
+  void SendFamilyLinkUserSettings(const base::DictionaryValue* settings);
 
   void OnTryURLResult(
       const std::map<std::string, base::string16>& allowlists,
+      const std::string& callback_id,
       SupervisedUserURLFilter::FilteringBehavior behavior,
       supervised_user_error_page::FilteringBehaviorReason reason,
       bool uncertain);
@@ -63,10 +65,10 @@
                           SupervisedUserURLFilter::Observer>
       scoped_observation_{this};
 
-  base::WeakPtrFactory<SupervisedUserInternalsMessageHandler> weak_factory_{
+  base::WeakPtrFactory<FamilyLinkUserInternalsMessageHandler> weak_factory_{
       this};
 
-  DISALLOW_COPY_AND_ASSIGN(SupervisedUserInternalsMessageHandler);
+  DISALLOW_COPY_AND_ASSIGN(FamilyLinkUserInternalsMessageHandler);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.cc b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.cc
new file mode 100644
index 0000000..92b5d0a1
--- /dev/null
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.cc
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.h"
+
+#include <memory>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/dev_ui_browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
+
+namespace {
+
+content::WebUIDataSource* CreateFamilyLinkUserInternalsHTMLSource() {
+  content::WebUIDataSource* source = content::WebUIDataSource::Create(
+      chrome::kChromeUIFamilyLinkUserInternalsHost);
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::ScriptSrc,
+      "script-src chrome://resources 'self' 'unsafe-eval';");
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::TrustedTypes,
+      "trusted-types jstemplate;");
+
+  source->AddResourcePath("family_link_user_internals.js",
+                          IDR_FAMILY_LINK_USER_INTERNALS_JS);
+  source->AddResourcePath("family_link_user_internals.css",
+                          IDR_FAMILY_LINK_USER_INTERNALS_CSS);
+  source->SetDefaultResource(IDR_FAMILY_LINK_USER_INTERNALS_HTML);
+  return source;
+}
+
+}  // namespace
+
+FamilyLinkUserInternalsUI::FamilyLinkUserInternalsUI(content::WebUI* web_ui)
+    : WebUIController(web_ui) {
+  Profile* profile = Profile::FromWebUI(web_ui);
+  content::WebUIDataSource::Add(profile,
+                                CreateFamilyLinkUserInternalsHTMLSource());
+
+  web_ui->AddMessageHandler(
+      std::make_unique<FamilyLinkUserInternalsMessageHandler>());
+}
+
+FamilyLinkUserInternalsUI::~FamilyLinkUserInternalsUI() {}
diff --git a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.h b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.h
new file mode 100644
index 0000000..0a129dfb
--- /dev/null
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_ui.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://family-link-user-internals page.
+class FamilyLinkUserInternalsUI : public content::WebUIController {
+ public:
+  explicit FamilyLinkUserInternalsUI(content::WebUI* web_ui);
+  ~FamilyLinkUserInternalsUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FamilyLinkUserInternalsUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_FAMILY_LINK_USER_INTERNALS_FAMILY_LINK_USER_INTERNALS_UI_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/about_section.cc b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
index ec99cb4..8a8384d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/about_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
@@ -105,7 +105,10 @@
        mojom::SearchResultDefaultRank::kMedium,
        mojom::SearchResultType::kSetting,
        {.setting = mojom::Setting::kDiagnostics},
-       {IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1, SearchConcept::kAltTagEnd}},
+       {IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT1,
+        IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT2,
+        IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT3,
+        IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4, SearchConcept::kAltTagEnd}},
   });
   return *tags;
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
index b9b9eb4..1c5867a6 100644
--- a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
@@ -11,12 +11,12 @@
 #include "base/command_line.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/ash/system/timezone_resolver_manager.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service.h"
 #include "chrome/browser/chromeos/set_time_dialog.h"
-#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/system_clock/system_clock_client.h"
 #include "chromeos/settings/timezone_settings.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc b/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
index 37705412..37af30c4 100644
--- a/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
@@ -7,8 +7,8 @@
 #include "base/no_destructor.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h
index adc1b28..676b25c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ash/system/pointer_device_observer.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 
 namespace base {
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_section.h b/chrome/browser/ui/webui/settings/chromeos/device_section.h
index 47bae70..7f05679 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_section.h
@@ -12,7 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ash/system/pointer_device_observer.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc b/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
index 5a3624c..70f09dd7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
@@ -9,6 +9,7 @@
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/child_accounts/child_user_service.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/browser_navigator.h"
@@ -71,7 +72,7 @@
     // Launch FLH app since it is available.
     proxy->Launch(app_id, ui::EventFlags::EF_NONE,
                   apps::mojom::LaunchSource::kFromParentalControls,
-                  display::kDefaultDisplayId);
+                  apps::MakeWindowInfo(display::kDefaultDisplayId));
     return;
   }
   // No FLH app installed, so try to launch Play Store to FLH app install page.
diff --git a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
index 1c0359a..94f0e039 100644
--- a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
@@ -256,6 +256,8 @@
        IDS_SETTINGS_PHOTO_DISCARD_ACCESSIBLE_TEXT},
       {"photoModeAccessibleText", IDS_SETTINGS_PHOTO_MODE_ACCESSIBLE_TEXT},
       {"videoModeAccessibleText", IDS_SETTINGS_VIDEO_MODE_ACCESSIBLE_TEXT},
+      // TODO(b/178399962) finalize error string for WallpaperWebUI.
+      {"wallpaperCollectionsError", IDS_WALLPAPER_MANAGER_NETWORK_ERROR},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc
index 60153b0..a1f17aa 100644
--- a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h"
 
 #include "base/bind.h"
+#include "chrome/browser/chromeos/backdrop_wallpaper_handlers/backdrop_wallpaper.pb.h"
+#include "chrome/browser/chromeos/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "content/public/browser/web_ui.h"
 
@@ -33,6 +35,11 @@
       "isWallpaperPolicyControlled",
       base::BindRepeating(&WallpaperHandler::HandleIsWallpaperPolicyControlled,
                           base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      "fetchWallpaperCollections",
+      base::BindRepeating(&WallpaperHandler::HandleFetchWallpaperCollections,
+                          base::Unretained(this)));
 }
 
 void WallpaperHandler::HandleIsWallpaperSettingVisible(
@@ -55,6 +62,36 @@
   WallpaperControllerClient::Get()->OpenWallpaperPickerIfAllowed();
 }
 
+void WallpaperHandler::HandleFetchWallpaperCollections(
+    const base::ListValue* args) {
+  CHECK_EQ(args->GetSize(), 1U);
+  DCHECK(IsJavascriptAllowed()) << "Page should already be initialized";
+  backdrop_api_weak_factory_.InvalidateWeakPtrs();
+  collection_info_fetcher_.Start(
+      base::BindOnce(&WallpaperHandler::OnFetchWallpaperCollections,
+                     backdrop_api_weak_factory_.GetWeakPtr(),
+                     args->GetList().front().Clone()));
+}
+
+void WallpaperHandler::OnFetchWallpaperCollections(
+    const base::Value& callback_id,
+    bool success,
+    const std::vector<backdrop::Collection>& collections) {
+  if (!success || collections.empty()) {
+    RejectJavascriptCallback(callback_id, base::Value(base::Value::Type::NONE));
+    return;
+  }
+
+  base::Value result(base::Value::Type::LIST);
+  for (const auto& collection : collections) {
+    base::Value item(base::Value::Type::DICTIONARY);
+    item.SetKey("id", base::Value(collection.collection_id()));
+    item.SetKey("name", base::Value(collection.collection_name()));
+    result.Append(std::move(item));
+  }
+  ResolveJavascriptCallback(callback_id, result);
+}
+
 void WallpaperHandler::ResolveCallback(const base::Value& callback_id,
                                        bool result) {
   AllowJavascript();
diff --git a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h
index 90fa8c6c..645671d0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h
@@ -6,11 +6,17 @@
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_WALLPAPER_HANDLER_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 
+namespace backdrop {
+class Collection;
+}  // namespace backdrop
+
 namespace base {
 class ListValue;
-}
+}  // namespace base
 
 namespace chromeos {
 namespace settings {
@@ -36,9 +42,21 @@
   // Open the wallpaper manager app.
   void HandleOpenWallpaperManager(const base::ListValue* args);
 
+  // Begin to fetch wallpaper collection info.
+  void HandleFetchWallpaperCollections(const base::ListValue* args);
+
+  void OnFetchWallpaperCollections(
+      const base::Value& callback_id,
+      bool success,
+      const std::vector<backdrop::Collection>& collections);
+
   // Helper function to resolve the Javascript callback.
   void ResolveCallback(const base::Value& callback_id, bool result);
 
+  backdrop_wallpaper_handlers::CollectionInfoFetcher collection_info_fetcher_;
+
+  base::WeakPtrFactory<WallpaperHandler> backdrop_api_weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(WallpaperHandler);
 };
 
diff --git a/chrome/browser/ui/webui/supervised_user_internals_ui.cc b/chrome/browser/ui/webui/supervised_user_internals_ui.cc
deleted file mode 100644
index 2102059..0000000
--- a/chrome/browser/ui/webui/supervised_user_internals_ui.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
-
-#include <memory>
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/supervised_user_internals_message_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/dev_ui_browser_resources.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "services/network/public/mojom/content_security_policy.mojom.h"
-
-namespace {
-
-content::WebUIDataSource* CreateSupervisedUserInternalsHTMLSource() {
-  content::WebUIDataSource* source = content::WebUIDataSource::Create(
-      chrome::kChromeUISupervisedUserInternalsHost);
-  source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::ScriptSrc,
-      "script-src chrome://resources 'self' 'unsafe-eval';");
-  source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::TrustedTypes,
-      "trusted-types jstemplate;");
-
-  source->AddResourcePath("supervised_user_internals.js",
-                          IDR_SUPERVISED_USER_INTERNALS_JS);
-  source->AddResourcePath("supervised_user_internals.css",
-                          IDR_SUPERVISED_USER_INTERNALS_CSS);
-  source->SetDefaultResource(IDR_SUPERVISED_USER_INTERNALS_HTML);
-  return source;
-}
-
-}  // namespace
-
-SupervisedUserInternalsUI::SupervisedUserInternalsUI(content::WebUI* web_ui)
-    : WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(profile,
-                                CreateSupervisedUserInternalsHTMLSource());
-
-  web_ui->AddMessageHandler(
-      std::make_unique<SupervisedUserInternalsMessageHandler>());
-}
-
-SupervisedUserInternalsUI::~SupervisedUserInternalsUI() {}
diff --git a/chrome/browser/ui/webui/supervised_user_internals_ui.h b/chrome/browser/ui/webui/supervised_user_internals_ui.h
deleted file mode 100644
index ff12e93..0000000
--- a/chrome/browser/ui/webui/supervised_user_internals_ui.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_controller.h"
-
-// The implementation for the chrome://supervised-user-internals page.
-class SupervisedUserInternalsUI : public content::WebUIController {
- public:
-  explicit SupervisedUserInternalsUI(content::WebUI* web_ui);
-  ~SupervisedUserInternalsUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SupervisedUserInternalsUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
diff --git a/chrome/browser/web_applications/components/external_install_options.cc b/chrome/browser/web_applications/components/external_install_options.cc
index 833d7b5..81d823c 100644
--- a/chrome/browser/web_applications/components/external_install_options.cc
+++ b/chrome/browser/web_applications/components/external_install_options.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/web_applications/components/system_web_app_types.h"
 
@@ -43,6 +44,7 @@
         options.install_url,
         options.user_display_mode,
         options.install_source,
+        options.fallback_app_name,
         options.add_to_applications_menu,
         options.add_to_desktop,
         options.add_to_quick_launch_bar,
@@ -110,6 +112,8 @@
          << "\n user_display_mode: " << install_options.user_display_mode
          << "\n install_source: "
          << static_cast<int32_t>(install_options.install_source)
+         << "\n fallback_app_name: "
+         << install_options.fallback_app_name.value_or("")
          << "\n add_to_applications_menu: "
          << install_options.add_to_applications_menu
          << "\n add_to_desktop: " << install_options.add_to_desktop
@@ -165,6 +169,11 @@
 
   params.user_display_mode = install_options.user_display_mode;
 
+  if (install_options.fallback_app_name.has_value()) {
+    params.fallback_app_name =
+        base::UTF8ToUTF16(install_options.fallback_app_name.value());
+  }
+
   params.fallback_start_url = install_options.install_url;
 
   params.add_to_applications_menu = install_options.add_to_applications_menu;
diff --git a/chrome/browser/web_applications/components/external_install_options.h b/chrome/browser/web_applications/components/external_install_options.h
index 836b0f9..a87c03b 100644
--- a/chrome/browser/web_applications/components/external_install_options.h
+++ b/chrome/browser/web_applications/components/external_install_options.h
@@ -39,6 +39,10 @@
   DisplayMode user_display_mode;
   ExternalInstallSource install_source;
 
+  // App name to use for placeholder apps or web apps that have no name in
+  // their manifest.
+  base::Optional<std::string> fallback_app_name;
+
   // If true, a shortcut is added to the Applications folder on macOS, and Start
   // Menu on Linux and Windows and launcher on Chrome OS. If false, we skip
   // adding a shortcut to desktop as well, regardless of the value of
@@ -123,7 +127,7 @@
   // metadata for the app. A placeholder app uses:
   //  - The default Chrome App icon for the icon
   //  - |url| as the start_url
-  //  - |url| as the app name
+  //  - |url| as the app name (unless fallback_app_name has been specified)
   bool install_placeholder = false;
 
   // Whether we should try to reinstall the app if there is a placeholder for
diff --git a/chrome/browser/web_applications/components/install_finalizer_unittest.cc b/chrome/browser/web_applications/components/install_finalizer_unittest.cc
index 1139051..6198c720 100644
--- a/chrome/browser/web_applications/components/install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/components/install_finalizer_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_prefs_utils.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/browser/web_applications/test/test_file_utils.h"
@@ -112,7 +113,60 @@
   FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
 
   EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code);
-  EXPECT_FALSE(result.installed_app_id.empty());
+  EXPECT_EQ(result.installed_app_id,
+            web_app::GenerateAppIdFromURL(info->start_url));
+}
+
+TEST_F(InstallFinalizerUnitTest, ConcurrentInstallSucceeds) {
+  auto info1 = std::make_unique<WebApplicationInfo>();
+  info1->start_url = GURL("https://foo1.example");
+  info1->title = base::ASCIIToUTF16("Foo1 Title");
+
+  auto info2 = std::make_unique<WebApplicationInfo>();
+  info2->start_url = GURL("https://foo2.example");
+  info2->title = base::ASCIIToUTF16("Foo2 Title");
+
+  InstallFinalizer::FinalizeOptions options;
+  options.install_source = webapps::WebappInstallSource::INTERNAL_DEFAULT;
+
+  base::RunLoop run_loop;
+  bool callback1_called = false;
+  bool callback2_called = false;
+
+  // Start install finalization for the 1st app.
+  {
+    finalizer().FinalizeInstall(
+        *info1, options,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                       web_app::InstallResultCode code) {
+          EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
+          EXPECT_EQ(installed_app_id,
+                    web_app::GenerateAppIdFromURL(info1->start_url));
+          callback1_called = true;
+          if (callback2_called)
+            run_loop.Quit();
+        }));
+  }
+
+  // Start install finalization for the 2nd app.
+  {
+    finalizer().FinalizeInstall(
+        *info2, options,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                       web_app::InstallResultCode code) {
+          EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
+          EXPECT_EQ(installed_app_id,
+                    web_app::GenerateAppIdFromURL(info2->start_url));
+          callback2_called = true;
+          if (callback1_called)
+            run_loop.Quit();
+        }));
+  }
+
+  run_loop.Run();
+
+  EXPECT_TRUE(callback1_called);
+  EXPECT_TRUE(callback2_called);
 }
 
 TEST_F(InstallFinalizerUnitTest, InstallStoresLatestWebAppInstallSource) {
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_constants.cc b/chrome/browser/web_applications/components/policy/web_app_policy_constants.cc
index 40189a0a..ce72c39 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_constants.cc
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_constants.cc
@@ -10,7 +10,8 @@
 const char kDefaultLaunchContainerKey[] = "default_launch_container";
 const char kDefaultLaunchContainerWindowValue[] = "window";
 const char kDefaultLaunchContainerTabValue[] = "tab";
-const char kCreateDesktopShorcutKey[] = "create_desktop_shortcut";
+const char kCreateDesktopShortcutKey[] = "create_desktop_shortcut";
+const char kFallbackAppNameKey[] = "fallback_app_name";
 
 const char kWildcard[] = "*";
 
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_constants.h b/chrome/browser/web_applications/components/policy/web_app_policy_constants.h
index 4673c1e..4e0de4d 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_constants.h
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_constants.h
@@ -12,7 +12,8 @@
 extern const char kDefaultLaunchContainerKey[];
 extern const char kDefaultLaunchContainerWindowValue[];
 extern const char kDefaultLaunchContainerTabValue[];
-extern const char kCreateDesktopShorcutKey[];
+extern const char kCreateDesktopShortcutKey[];
+extern const char kFallbackAppNameKey[];
 
 extern const char kWildcard[];
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
index 4fc9aa7..e4e13cb 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -55,6 +55,8 @@
 // Do not add tests to this class. Instead, add tests to
 // |InstallFinalizerUnitTest| so that both |InstallFinalizer| implementations
 // are tested.
+// TODO(crbug.com/1068081): Migrate remaining tests to
+// install_finalizer_unittest.
 class BookmarkAppInstallFinalizerTest : public ChromeRenderViewHostTestHarness {
  public:
   // Subclass that runs a closure when an extension is unpacked successfully.
@@ -217,57 +219,6 @@
   EXPECT_TRUE(callback_called);
 }
 
-TEST_F(BookmarkAppInstallFinalizerTest, ConcurrentInstallSucceeds) {
-  base::RunLoop run_loop;
-
-  const GURL url1("https://foo1.example");
-  const GURL url2("https://foo2.example");
-
-  bool callback1_called = false;
-  bool callback2_called = false;
-  web_app::InstallFinalizer::FinalizeOptions options;
-  options.install_source = webapps::WebappInstallSource::INTERNAL_DEFAULT;
-
-  // Start install finalization for the 1st app
-  {
-    WebApplicationInfo web_application_info;
-    web_application_info.start_url = url1;
-
-    finalizer().FinalizeInstall(
-        web_application_info, options,
-        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
-                                       web_app::InstallResultCode code) {
-          EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
-          EXPECT_EQ(installed_app_id, web_app::GenerateAppIdFromURL(url1));
-          callback1_called = true;
-          if (callback2_called)
-            run_loop.Quit();
-        }));
-  }
-
-  // Start install finalization for the 2nd app
-  {
-    WebApplicationInfo web_application_info;
-    web_application_info.start_url = url2;
-
-    finalizer().FinalizeInstall(
-        web_application_info, options,
-        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
-                                       web_app::InstallResultCode code) {
-          EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
-          EXPECT_EQ(installed_app_id, web_app::GenerateAppIdFromURL(url2));
-          callback2_called = true;
-          if (callback1_called)
-            run_loop.Quit();
-        }));
-  }
-
-  run_loop.Run();
-
-  EXPECT_TRUE(callback1_called);
-  EXPECT_TRUE(callback2_called);
-}
-
 TEST_F(BookmarkAppInstallFinalizerTest, DefaultInstalledSucceeds) {
   auto info = std::make_unique<WebApplicationInfo>();
   info->start_url = WebAppUrl();
diff --git a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
index d4f32f7..30c31ae 100644
--- a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
@@ -64,6 +64,8 @@
   }
 })";
 
+const char kDefaultFallbackAppName[] = "fallback app name";
+
 // TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
 // function.
 GURL WindowedUrl() {
@@ -134,13 +136,13 @@
   return options;
 }
 
-base::Value GetCreateDesktopShorcutDefaultItem() {
+base::Value GetCreateDesktopShortcutDefaultItem() {
   base::Value item(base::Value::Type::DICTIONARY);
   item.SetKey(kUrlKey, base::Value(NoContainerUrl().spec()));
   return item;
 }
 
-ExternalInstallOptions GetCreateDesktopShorcutDefaultInstallOptions() {
+ExternalInstallOptions GetCreateDesktopShortcutDefaultInstallOptions() {
   ExternalInstallOptions options(NoContainerUrl(), DisplayMode::kBrowser,
                                  ExternalInstallSource::kExternalPolicy);
   options.add_to_applications_menu = true;
@@ -152,14 +154,14 @@
   return options;
 }
 
-base::Value GetCreateDesktopShorcutFalseItem() {
+base::Value GetCreateDesktopShortcutFalseItem() {
   base::Value item(base::Value::Type::DICTIONARY);
   item.SetKey(kUrlKey, base::Value(NoContainerUrl().spec()));
-  item.SetKey(kCreateDesktopShorcutKey, base::Value(false));
+  item.SetKey(kCreateDesktopShortcutKey, base::Value(false));
   return item;
 }
 
-ExternalInstallOptions GetCreateDesktopShorcutFalseInstallOptions() {
+ExternalInstallOptions GetCreateDesktopShortcutFalseInstallOptions() {
   ExternalInstallOptions options(NoContainerUrl(), DisplayMode::kBrowser,
                                  ExternalInstallSource::kExternalPolicy);
   options.add_to_applications_menu = true;
@@ -171,14 +173,14 @@
   return options;
 }
 
-base::Value GetCreateDesktopShorcutTrueItem() {
+base::Value GetCreateDesktopShortcutTrueItem() {
   base::Value item(base::Value::Type::DICTIONARY);
   item.SetKey(kUrlKey, base::Value(NoContainerUrl().spec()));
-  item.SetKey(kCreateDesktopShorcutKey, base::Value(true));
+  item.SetKey(kCreateDesktopShortcutKey, base::Value(true));
   return item;
 }
 
-ExternalInstallOptions GetCreateDesktopShorcutTrueInstallOptions() {
+ExternalInstallOptions GetCreateDesktopShortcutTrueInstallOptions() {
   ExternalInstallOptions options(NoContainerUrl(), DisplayMode::kBrowser,
                                  ExternalInstallSource::kExternalPolicy);
   options.add_to_applications_menu = true;
@@ -202,6 +204,28 @@
   int on_policy_changed_call_count = 0;
 };
 
+base::Value GetFallbackAppNameItem() {
+  base::Value item(base::Value::Type::DICTIONARY);
+  item.SetKey(kUrlKey, base::Value(WindowedUrl().spec()));
+  item.SetKey(kDefaultLaunchContainerKey,
+              base::Value(kDefaultLaunchContainerWindowValue));
+  item.SetKey(kFallbackAppNameKey, base::Value(kDefaultFallbackAppName));
+  return item;
+}
+
+ExternalInstallOptions GetFallbackAppNameInstallOptions() {
+  ExternalInstallOptions options(WindowedUrl(), DisplayMode::kStandalone,
+                                 ExternalInstallSource::kExternalPolicy);
+  options.add_to_applications_menu = true;
+  options.add_to_desktop = false;
+  options.add_to_quick_launch_bar = false;
+  options.install_placeholder = true;
+  options.reinstall_placeholder = true;
+  options.wait_for_windows_closed = true;
+  options.fallback_app_name = kDefaultFallbackAppName;
+  return options;
+}
+
 }  // namespace
 
 class WebAppPolicyManagerTest : public ChromeRenderViewHostTestHarness {
@@ -446,9 +470,9 @@
 }
 
 TEST_F(WebAppPolicyManagerTest,
-       ForceInstallAppWithDefaultCreateDesktopShorcut) {
+       ForceInstallAppWithDefaultCreateDesktopShortcut) {
   base::Value list(base::Value::Type::LIST);
-  list.Append(GetCreateDesktopShorcutDefaultItem());
+  list.Append(GetCreateDesktopShortcutDefaultItem());
   profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
 
   policy_manager()->Start();
@@ -458,15 +482,15 @@
 
   std::vector<ExternalInstallOptions> expected_install_options_list;
   expected_install_options_list.push_back(
-      GetCreateDesktopShorcutDefaultInstallOptions());
+      GetCreateDesktopShortcutDefaultInstallOptions());
 
   EXPECT_EQ(install_requests, expected_install_options_list);
 }
 
 TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithCreateDesktopShortcut) {
   base::Value list(base::Value::Type::LIST);
-  list.Append(GetCreateDesktopShorcutFalseItem());
-  list.Append(GetCreateDesktopShorcutTrueItem());
+  list.Append(GetCreateDesktopShortcutFalseItem());
+  list.Append(GetCreateDesktopShortcutTrueItem());
   profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
 
   policy_manager()->Start();
@@ -476,9 +500,25 @@
 
   std::vector<ExternalInstallOptions> expected_install_options_list;
   expected_install_options_list.push_back(
-      GetCreateDesktopShorcutFalseInstallOptions());
+      GetCreateDesktopShortcutFalseInstallOptions());
   expected_install_options_list.push_back(
-      GetCreateDesktopShorcutTrueInstallOptions());
+      GetCreateDesktopShortcutTrueInstallOptions());
+
+  EXPECT_EQ(install_requests, expected_install_options_list);
+}
+
+TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithFallbackAppName) {
+  base::Value list(base::Value::Type::LIST);
+  list.Append(GetFallbackAppNameItem());
+  profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+  policy_manager()->Start();
+  base::RunLoop().RunUntilIdle();
+
+  const auto& install_requests = pending_app_manager()->install_requests();
+
+  std::vector<ExternalInstallOptions> expected_install_options_list;
+  expected_install_options_list.push_back(GetFallbackAppNameInstallOptions());
 
   EXPECT_EQ(install_requests, expected_install_options_list);
 }
@@ -607,6 +647,34 @@
   EXPECT_EQ(expected_options_list, install_options_list);
 }
 
+// Tests that we correctly reinstall a placeholder app when the placeholder
+// is using a fallback name.
+TEST_F(WebAppPolicyManagerTest, ReinstallPlaceholderAppWithFallbackAppName) {
+  base::Value list(base::Value::Type::LIST);
+  list.Append(GetFallbackAppNameItem());
+  profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+  policy_manager()->Start();
+  base::RunLoop().RunUntilIdle();
+
+  std::vector<ExternalInstallOptions> expected_options_list;
+  expected_options_list.push_back(GetFallbackAppNameInstallOptions());
+
+  const auto& install_options_list = pending_app_manager()->install_requests();
+  EXPECT_EQ(expected_options_list, install_options_list);
+
+  policy_manager()->ReinstallPlaceholderAppIfNecessary(WindowedUrl());
+  base::RunLoop().RunUntilIdle();
+
+  auto reinstall_options = GetFallbackAppNameInstallOptions();
+  reinstall_options.install_placeholder = false;
+  reinstall_options.reinstall_placeholder = true;
+  reinstall_options.wait_for_windows_closed = true;
+  expected_options_list.push_back(std::move(reinstall_options));
+
+  EXPECT_EQ(expected_options_list, install_options_list);
+}
+
 TEST_F(WebAppPolicyManagerTest, TryToInexistentPlaceholderApp) {
   base::Value list(base::Value::Type::LIST);
   list.Append(GetWindowedItem());
diff --git a/chrome/browser/web_applications/pending_app_install_task.cc b/chrome/browser/web_applications/pending_app_install_task.cc
index 29fe541..f81d4bd7 100644
--- a/chrome/browser/web_applications/pending_app_install_task.cc
+++ b/chrome/browser/web_applications/pending_app_install_task.cc
@@ -232,7 +232,10 @@
   }
 
   WebApplicationInfo web_app_info;
-  web_app_info.title = base::UTF8ToUTF16(install_options_.install_url.spec());
+  web_app_info.title =
+      install_options_.fallback_app_name
+          ? base::UTF8ToUTF16(install_options_.fallback_app_name.value())
+          : base::UTF8ToUTF16(install_options_.install_url.spec());
   web_app_info.start_url = install_options_.install_url;
 
   switch (install_options_.user_display_mode) {
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index 3aeabad..f60991b 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -43,7 +43,8 @@
   const base::Value* default_launch_container =
       entry.FindKey(kDefaultLaunchContainerKey);
   const base::Value* create_desktop_shortcut =
-      entry.FindKey(kCreateDesktopShorcutKey);
+      entry.FindKey(kCreateDesktopShortcutKey);
+  const base::Value* fallback_app_name = entry.FindKey(kFallbackAppNameKey);
 
   DCHECK(!default_launch_container ||
          default_launch_container->GetString() ==
@@ -72,6 +73,11 @@
   // policy.
   install_options.add_to_quick_launch_bar = false;
 
+  // Allow administrators to override the name of the placeholder app, as well
+  // as the permanent name for Web Apps without a manifest.
+  if (fallback_app_name)
+    install_options.fallback_app_name = fallback_app_name->GetString();
+
   return install_options;
 }
 
diff --git a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
index 8ceb14f..ddb30823 100644
--- a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/file_system_access/file_system_access_permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -328,7 +329,7 @@
   proxy->Launch(GetManager().GetAppIdForSystemApp(GetMockAppType()).value(),
                 ui::EventFlags::EF_NONE,
                 apps::mojom::LaunchSource::kFromAppListGrid,
-                display::kDefaultDisplayId);
+                apps::MakeWindowInfo(display::kDefaultDisplayId));
   navigation_observer.Wait();
 
   histograms.ExpectTotalCount("Apps.DefaultAppLaunch.FromAppListGrid", 1);
@@ -352,7 +353,8 @@
   proxy->LaunchAppWithIntent(
       GetManager().GetAppIdForSystemApp(GetMockAppType()).value(),
       ui::EventFlags::EF_NONE, std::move(intent),
-      apps::mojom::LaunchSource::kFromAppListGrid, display::kDefaultDisplayId);
+      apps::mojom::LaunchSource::kFromAppListGrid,
+      apps::MakeWindowInfo(display::kDefaultDisplayId));
   navigation_observer.Wait();
 
   histograms.ExpectTotalCount("Apps.DefaultAppLaunch.FromAppListGrid", 1);
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 82fb6d9..79a3670f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1612958227-41d24d7235743aac0398667eab01a70e18222ac3.profdata
+chrome-linux-master-1613001202-6ef218cbf7480c8ae793fe9b5cc2ae6ec8870c75.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index d134033b..c9db1cf 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1612958227-64f4414608b36e2c298433af63d65254883eddd6.profdata
+chrome-mac-master-1612979846-d7c647575837d460d0c274770ac2364327206664.profdata
diff --git a/chrome/chrome_cleaner/executables/BUILD.gn b/chrome/chrome_cleaner/executables/BUILD.gn
index 13ce260..67d5223 100644
--- a/chrome/chrome_cleaner/executables/BUILD.gn
+++ b/chrome/chrome_cleaner/executables/BUILD.gn
@@ -90,6 +90,15 @@
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
+
+    data_deps = []
+    if (is_internal_chrome_cleaner_build) {
+      # Official and devel builders need to upload symbols to the crash server.
+      data_deps += [ "//third_party/breakpad:symupload" ]
+    }
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
   }
 }
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 3b4ac91f..8dd87dc 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -994,4 +994,8 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+// Enables link to text to be generated in advance.
+const base::Feature kPreemtiveLinkToTextGeneration{
+    "PreemtiveLinkToTextGeneration", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index f18a1d70..dac8736 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -118,6 +118,9 @@
 #endif
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kPreemtiveLinkToTextGeneration;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kPrivacySandboxSettings;
 
 #if defined(OS_ANDROID)
diff --git a/chrome/common/extensions/api/scripting.idl b/chrome/common/extensions/api/scripting.idl
index ce1512ff..7607855 100644
--- a/chrome/common/extensions/api/scripting.idl
+++ b/chrome/common/extensions/api/scripting.idl
@@ -37,8 +37,8 @@
     // specified.
     [serializableFunction]InjectedFunction? function;
 
-    // The path of the JS files to inject, relative to the extension's root
-    // directory.
+    // The path of the JS or CSS files to inject, relative to the extension's
+    // root directory.
     // NOTE: Currently a maximum of one file is supported.
     // Exactly one of <code>files</code> and <code>function</code> must be
     // specified.
@@ -96,5 +96,15 @@
     // |callback|: Invoked upon completion of the insertion.
     static void insertCSS(CSSInjection injection,
                           optional CSSInjectionCallback callback);
+
+    // Removes a CSS stylesheet that was previously inserted by this extension
+    // from a target context.
+    // |injection|: The details of the styles to remove. Note that the
+    // <code>css</code>, <code>files</code>, and <code>origin</code> properties
+    // must exactly match the stylesheet inserted through $(ref:insertCSS).
+    // Attempting to remove a non-existent stylesheet is a no-op.
+    // |callback|: A callback to be invoked upon the completion of the removal.
+    static void removeCSS(CSSInjection injection,
+                          optional CSSInjectionCallback callback);
   };
 };
diff --git a/chrome/common/printing/printer_capabilities.cc b/chrome/common/printing/printer_capabilities.cc
index 987ba2e..e89392e 100644
--- a/chrome/common/printing/printer_capabilities.cc
+++ b/chrome/common/printing/printer_capabilities.cc
@@ -33,10 +33,8 @@
 #endif  // defined(OS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/feature_list.h"
 #include "chrome/common/printing/ipp_l10n.h"
 #include "components/strings/grit/components_strings.h"
-#include "printing/printing_features.h"
 #include "ui/base/l10n/l10n_util.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -122,8 +120,7 @@
   if (!has_secure_protocol)
     caps->pin_supported = false;
 
-  if (base::FeatureList::IsEnabled(printing::features::kAdvancedPpdAttributes))
-    PopulateAdvancedCapsLocalization(&caps->advanced_capabilities);
+  PopulateAdvancedCapsLocalization(&caps->advanced_capabilities);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return cloud_print::PrinterSemanticCapsAndDefaultsToCdd(*caps);
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 767a9fd..72a1947 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -77,6 +77,10 @@
 const char kChromeUIExtensionsHost[] = "extensions";
 const char kChromeUIExtensionsInternalsHost[] = "extensions-internals";
 const char kChromeUIExtensionsURL[] = "chrome://extensions/";
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+const char kChromeUIFamilyLinkUserInternalsHost[] =
+    "family-link-user-internals";
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 const char kChromeUIFaviconHost[] = "favicon";
 const char kChromeUIFaviconURL[] = "chrome://favicon/";
 const char kChromeUIFavicon2Host[] = "favicon2";
@@ -159,7 +163,6 @@
 const char kChromeUISiteEngagementHost[] = "site-engagement";
 const char kChromeUISuggestionsHost[] = "suggestions";
 const char kChromeUISuggestionsURL[] = "chrome://suggestions/";
-const char kChromeUISupervisedUserInternalsHost[] = "supervised-user-internals";
 const char kChromeUISupervisedUserPassphrasePageHost[] =
     "managed-user-passphrase";
 const char kChromeUISyncConfirmationHost[] = "sync-confirmation";
@@ -474,6 +477,9 @@
 #endif
     kChromeUIDeviceLogHost,
     kChromeUIDownloadInternalsHost,
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+    kChromeUIFamilyLinkUserInternalsHost,
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
     kChromeUIFlagsHost,
     kChromeUIGCMInternalsHost,
     kChromeUIHistoryHost,
@@ -502,7 +508,6 @@
     kChromeUINTPTilesInternalsHost,
     safe_browsing::kChromeUISafeBrowsingHost,
     kChromeUISuggestionsHost,
-    kChromeUISupervisedUserInternalsHost,
     kChromeUISyncInternalsHost,
 #if !defined(OS_ANDROID)
     kChromeUITermsHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 7c8b02a..1823d8ac 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -79,6 +79,9 @@
 extern const char kChromeUIExtensionsHost[];
 extern const char kChromeUIExtensionsInternalsHost[];
 extern const char kChromeUIExtensionsURL[];
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+extern const char kChromeUIFamilyLinkUserInternalsHost[];
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 extern const char kChromeUIFaviconHost[];
 extern const char kChromeUIFaviconURL[];
 extern const char kChromeUIFavicon2Host[];
@@ -159,7 +162,6 @@
 extern const char kChromeUISiteEngagementHost[];
 extern const char kChromeUISuggestionsHost[];
 extern const char kChromeUISuggestionsURL[];
-extern const char kChromeUISupervisedUserInternalsHost[];
 extern const char kChromeUISupervisedUserPassphrasePageHost[];
 extern const char kChromeUISyncConfirmationHost[];
 extern const char kChromeUISyncConfirmationURL[];
diff --git a/chrome/renderer/chrome_render_frame_observer_browsertest.cc b/chrome/renderer/chrome_render_frame_observer_browsertest.cc
index a8f419d3..e3944ed6 100644
--- a/chrome/renderer/chrome_render_frame_observer_browsertest.cc
+++ b/chrome/renderer/chrome_render_frame_observer_browsertest.cc
@@ -50,6 +50,8 @@
     called_new_page_ = true;
     page_level_translation_critiera_met_ = page_level_translation_critiera_met;
   }
+  void GetLanguageDetectionModel(
+      GetLanguageDetectionModelCallback callback) override {}
 
   bool called_new_page_ = false;
   bool page_level_translation_critiera_met_ = false;
diff --git a/chrome/renderer/translate/translate_agent_browsertest.cc b/chrome/renderer/translate/translate_agent_browsertest.cc
index 893e5361..4121cab 100644
--- a/chrome/renderer/translate/translate_agent_browsertest.cc
+++ b/chrome/renderer/translate/translate_agent_browsertest.cc
@@ -51,6 +51,8 @@
     details_ = details;
     page_level_translation_critiera_met_ = page_level_translation_critiera_met;
   }
+  void GetLanguageDetectionModel(
+      GetLanguageDetectionModelCallback callback) override {}
 
   void ResetNewPageValues() {
     called_new_page_ = false;
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc
index 7e38b85..5b8b8d5 100644
--- a/chrome/services/sharing/nearby/nearby_connections.cc
+++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -290,7 +290,10 @@
   }
   VLOG(1) << "Nearby Connections: waiting for Core objects to finish stopping "
           << "all endpoints.";
-  latch.Await(absl::Seconds(5));
+  if (!latch.Await(absl::Seconds(5)).result()) {
+    LOG(FATAL) << __func__ << ": Failed to stop all endpoints on each Core in "
+               << "time. Look for deadlocks in the threads tab of this crash.";
+  }
 
   VLOG(1) << "Nearby Connections: shutting down the shared service controller "
           << "prior to taking down Core objects";
@@ -301,6 +304,8 @@
   VLOG(1) << "Nearby Connections: cleaning up Core objects";
   service_id_to_core_map_.clear();
   g_instance = nullptr;
+
+  VLOG(1) << "Nearby Connections: shutdown complete";
 }
 
 void NearbyConnections::OnDisconnect(const std::string dependency_name) {
diff --git a/chrome/services/speech/BUILD.gn b/chrome/services/speech/BUILD.gn
index 77e713e7..a6c39b5 100644
--- a/chrome/services/speech/BUILD.gn
+++ b/chrome/services/speech/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/buildflag_header.gni")
+import("//build/config/chromeos/ui_mode.gni")
 import("//chrome/services/speech/buildflags.gni")
 
 buildflag_header("buildflags") {
@@ -20,6 +21,13 @@
     "speech_recognition_service_impl.h",
   ]
 
+  if (is_chromeos_ash) {
+    sources += [
+      "cros_speech_recognition_recognizer_impl.cc",
+      "cros_speech_recognition_recognizer_impl.h",
+    ]
+  }
+
   public_deps = [
     "//media/mojo/mojom",
     "//mojo/public/cpp/bindings",
@@ -30,6 +38,7 @@
     ":buildflags",
     "//base",
     "//chrome/services/speech/soda",
+    "//chrome/services/speech/soda:soda_api_proto",
     "//components/speech",
     "//content/public/browser:proto",
     "//google_apis",
diff --git a/chrome/services/speech/DEPS b/chrome/services/speech/DEPS
index 8afeca3e..6b4b926 100644
--- a/chrome/services/speech/DEPS
+++ b/chrome/services/speech/DEPS
@@ -1,5 +1,7 @@
 include_rules = [
   "+chrome/services/speech",
+  "+chromeos/services/machine_learning/public/cpp",
+  "+chromeos/services/machine_learning/public/mojom",
   "+components/speech",
   "+content/public/browser",
   "+google_apis",
diff --git a/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
new file mode 100644
index 0000000..344026f4
--- /dev/null
+++ b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
@@ -0,0 +1,97 @@
+// Copyright 2021 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/speech/cros_speech_recognition_recognizer_impl.h"
+
+#include "chrome/services/speech/soda/cros_soda_client.h"
+#include "google_apis/google_api_keys.h"
+#include "media/base/audio_buffer.h"
+#include "media/base/audio_sample_types.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+namespace speech {
+constexpr char kNoClientError[] = "No cros soda client.";
+
+void CrosSpeechRecognitionRecognizerImpl::Create(
+    mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
+    mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> remote,
+    base::WeakPtr<SpeechRecognitionServiceImpl> speech_recognition_service_impl,
+    const base::FilePath& binary_path,
+    const base::FilePath& config_path) {
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<CrosSpeechRecognitionRecognizerImpl>(
+          std::move(remote), std::move(speech_recognition_service_impl),
+          binary_path, config_path),
+      std::move(receiver));
+}
+CrosSpeechRecognitionRecognizerImpl::~CrosSpeechRecognitionRecognizerImpl() =
+    default;
+
+CrosSpeechRecognitionRecognizerImpl::CrosSpeechRecognitionRecognizerImpl(
+    mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> remote,
+    base::WeakPtr<SpeechRecognitionServiceImpl> speech_recognition_service_impl,
+    const base::FilePath& binary_path,
+    const base::FilePath& config_path)
+    : SpeechRecognitionRecognizerImpl(
+          std::move(remote),
+          std::move(speech_recognition_service_impl),
+          binary_path,
+          config_path),
+      enable_soda_(
+          base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {
+  recognition_event_callback_ = base::BindRepeating(
+      &CrosSpeechRecognitionRecognizerImpl::OnRecognitionEvent,
+      weak_factory_.GetWeakPtr());
+  DCHECK(enable_soda_) << "This class is only expected to run with soda "
+                          "enabled, but it can without.";
+  if (enable_soda_) {
+    cros_soda_client_ = std::make_unique<soda::CrosSodaClient>();
+  }
+}
+
+void CrosSpeechRecognitionRecognizerImpl::
+    SendAudioToSpeechRecognitionServiceInternal(
+        media::mojom::AudioDataS16Ptr buffer) {
+  if (!enable_soda_) {
+    // Defer to the superclass.
+    LOG(DFATAL) << "This class is only expected to be used when soda is "
+                   "enabled; Deferring to superclass.";
+    SpeechRecognitionRecognizerImpl::
+        SendAudioToSpeechRecognitionServiceInternal(std::move(buffer));
+    return;
+  }
+
+  // Soda is on, let's send the audio to it.
+  int channel_count = buffer->channel_count;
+  int sample_rate = buffer->sample_rate;
+  size_t buffer_size = 0;
+  // Verify and calculate the buffer size.
+  if (!base::CheckMul(buffer->data.size(), sizeof(buffer->data[0]))
+           .AssignIfValid(&buffer_size)) {
+    LOG(DFATAL) << "Size check invalid.";
+    return;
+  }
+  if (cros_soda_client_ == nullptr) {
+    LOG(DFATAL) << "No soda client, stopping.";
+    mojo::ReportBadMessage(kNoClientError);
+    return;
+  }
+
+  if (!cros_soda_client_->IsInitialized() ||
+      cros_soda_client_->DidAudioPropertyChange(sample_rate, channel_count)) {
+    auto config = chromeos::machine_learning::mojom::SodaConfig::New();
+    config->channel_count = channel_count;
+    config->sample_rate = sample_rate;
+    config->api_key = google_apis::GetSodaAPIKey();
+    // TODO(robsc): add in library locations.
+    cros_soda_client_->Reset(std::move(config), recognition_event_callback_);
+  }
+  cros_soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
+                              buffer_size);
+}
+}  // namespace speech
diff --git a/chrome/services/speech/cros_speech_recognition_recognizer_impl.h b/chrome/services/speech/cros_speech_recognition_recognizer_impl.h
new file mode 100644
index 0000000..61d7377
--- /dev/null
+++ b/chrome/services/speech/cros_speech_recognition_recognizer_impl.h
@@ -0,0 +1,68 @@
+// Copyright 2021 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_SPEECH_CROS_SPEECH_RECOGNITION_RECOGNIZER_IMPL_H_
+#define CHROME_SERVICES_SPEECH_CROS_SPEECH_RECOGNITION_RECOGNIZER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/services/speech/cloud_speech_recognition_client.h"
+#include "chrome/services/speech/speech_recognition_recognizer_impl.h"
+#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace soda {
+class CrosSodaClient;
+}  // namespace soda
+
+namespace speech {
+// Implementation of SpeechRecognitionRecognizer specifically for ChromeOS and
+// SODA. The implementation forwards and works with SODA that actually runs in
+// the ML Service; The Web instantiation should not be used, and defers to the
+// superclass.
+class CrosSpeechRecognitionRecognizerImpl
+    : public SpeechRecognitionRecognizerImpl {
+ public:
+  CrosSpeechRecognitionRecognizerImpl(
+      mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+          remote,
+      base::WeakPtr<SpeechRecognitionServiceImpl>
+          speech_recognition_service_impl,
+      const base::FilePath& binary_path,
+      const base::FilePath& config_path);
+  ~CrosSpeechRecognitionRecognizerImpl() override;
+
+  static void Create(
+      mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
+      mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+          remote,
+      base::WeakPtr<SpeechRecognitionServiceImpl>
+          speech_recognition_service_impl,
+      const base::FilePath& binary_path,
+      const base::FilePath& config_path);
+
+  OnRecognitionEventCallback recognition_event_callback() const {
+    return recognition_event_callback_;
+  }
+  // SpeechRecognitionRecognizerImpl:
+  void SendAudioToSpeechRecognitionServiceInternal(
+      media::mojom::AudioDataS16Ptr buffer) override;
+
+ private:
+  std::unique_ptr<soda::CrosSodaClient> cros_soda_client_;
+  // The callback that is eventually executed on a speech recognition event
+  // which passes the transcribed audio back to the caller via the speech
+  // recognition event client remote.
+  OnRecognitionEventCallback recognition_event_callback_;
+
+  const bool enable_soda_;
+
+  base::WeakPtrFactory<CrosSpeechRecognitionRecognizerImpl> weak_factory_{this};
+};
+}  // namespace speech
+
+#endif  // CHROME_SERVICES_SPEECH_CROS_SPEECH_RECOGNITION_RECOGNIZER_IMPL_H_
diff --git a/chrome/services/speech/soda/BUILD.gn b/chrome/services/speech/soda/BUILD.gn
index 6e4437d2..c2bfd00 100644
--- a/chrome/services/speech/soda/BUILD.gn
+++ b/chrome/services/speech/soda/BUILD.gn
@@ -1,7 +1,13 @@
 # Copyright 2020 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/chromeos/ui_mode.gni")
 import("//chrome/services/speech/buildflags.gni")
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("soda_api_proto") {
+  sources = [ "proto/soda_api.proto" ]
+}
 
 source_set("soda") {
   sources = [
@@ -10,9 +16,24 @@
     "soda_client.h",
   ]
 
-  deps = [ "//base" ]
+  deps = [
+    ":soda_api_proto",
+    "//base",
+  ]
+
+  if (is_chromeos_ash) {
+    sources += [
+      "cros_soda_client.cc",
+      "cros_soda_client.h",
+    ]
+    deps += [
+      "//chromeos/services/machine_learning/public/cpp",
+      "//chromeos/services/machine_learning/public/mojom",
+    ]
+  }
 }
 
+# TODO(robsc): add unit test for cros_soda_client.
 source_set("unit_tests") {
   testonly = true
 
diff --git a/chrome/services/speech/soda/cros_soda_client.cc b/chrome/services/speech/soda/cros_soda_client.cc
new file mode 100644
index 0000000..f29f2dbc
--- /dev/null
+++ b/chrome/services/speech/soda/cros_soda_client.cc
@@ -0,0 +1,87 @@
+// Copyright 2021 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/speech/soda/cros_soda_client.h"
+#include "base/run_loop.h"
+#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace soda {
+CrosSodaClient::CrosSodaClient() : soda_client_(this) {}
+CrosSodaClient::~CrosSodaClient() = default;
+
+bool CrosSodaClient::DidAudioPropertyChange(int sample_rate,
+                                            int channel_count) {
+  return !is_initialized_ || sample_rate_ != sample_rate ||
+         channel_count_ != channel_count;
+}
+
+void CrosSodaClient::AddAudio(const char* audio_buffer,
+                              int audio_buffer_size) const {
+  DCHECK(IsInitialized()) << "Unable to add audio before starting.";
+  const uint8_t* audio_buffer_casted =
+      reinterpret_cast<const uint8_t*>(audio_buffer);
+  std::vector<uint8_t> audio(audio_buffer_casted,
+                             audio_buffer_casted + audio_buffer_size);
+  soda_recognizer_->AddAudio(audio);
+}
+
+void CrosSodaClient::Reset(
+    chromeos::machine_learning::mojom::SodaConfigPtr soda_config,
+    base::RepeatingCallback<void(const std::string&, bool)> callback) {
+  sample_rate_ = soda_config->sample_rate;
+  channel_count_ = soda_config->channel_count;
+  if (is_initialized_) {
+    soda_recognizer_->Stop();
+  }
+  soda_recognizer_.reset();
+  soda_client_.reset();
+  is_initialized_ = true;
+  auto mojom_config = chromeos::machine_learning::mojom::SodaConfig::New();
+  mojom_config->channel_count = channel_count_;
+  mojom_config->sample_rate = sample_rate_;
+
+  chromeos::machine_learning::ServiceConnection::GetInstance()
+      ->GetMachineLearningService()
+      .LoadSpeechRecognizer(
+          std::move(mojom_config), soda_client_.BindNewPipeAndPassRemote(),
+          soda_recognizer_.BindNewPipeAndPassReceiver(),
+          base::BindOnce(
+              [](chromeos::machine_learning::mojom::LoadModelResult result) {
+                if (result !=
+                    chromeos::machine_learning::mojom::LoadModelResult::OK) {
+                  LOG(DFATAL) << "Could not load recognizer, error: " << result;
+                }
+              }));
+
+  callback_ = callback;
+  // Ensure this one is started.
+  soda_recognizer_->Start();
+}
+void CrosSodaClient::OnStop() {
+  // Do nothing OnStop.
+}
+void CrosSodaClient::OnStart() {
+  // Do nothing OnStart.
+}
+void CrosSodaClient::OnSpeechRecognizerEvent(
+    chromeos::machine_learning::mojom::SpeechRecognizerEventPtr event) {
+  if (event->is_final_result()) {
+    auto& final_result = event->get_final_result();
+    if (!final_result->final_hypotheses.empty()) {
+      const std::string final_hyp = final_result->final_hypotheses.front();
+      callback_.Run(final_hyp, true);
+    }
+  } else if (event->is_partial_result()) {
+    auto& partial_result = event->get_partial_result();
+    if (!partial_result->partial_text.empty()) {
+      const std::string partial_hyp = partial_result->partial_text.front();
+      callback_.Run(partial_hyp, false);
+    }
+  } else {
+    LOG(ERROR) << "Some kind of other soda event, ignoring completely.";
+  }
+}
+
+}  // namespace soda
diff --git a/chrome/services/speech/soda/cros_soda_client.h b/chrome/services/speech/soda/cros_soda_client.h
new file mode 100644
index 0000000..dc0f714
--- /dev/null
+++ b/chrome/services/speech/soda/cros_soda_client.h
@@ -0,0 +1,59 @@
+// Copyright 2021 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_SPEECH_SODA_CROS_SODA_CLIENT_H_
+#define CHROME_SERVICES_SPEECH_SODA_CROS_SODA_CLIENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "chromeos/services/machine_learning/public/mojom/soda.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace soda {
+
+// Client that wraps the ML Service connection for SODA on Chrome.
+// TODO(robsc@chromium): Move this to
+// chromeos/services/machine_learning/public/cpp as SodaClient.
+class CrosSodaClient : public chromeos::machine_learning::mojom::SodaClient {
+ public:
+  CrosSodaClient();
+  ~CrosSodaClient() override;
+
+  // Adds audio to this soda client. Only makes sense when initialized.
+  // Eventually, asynchronous callbacks to the ::SodaClient overrides below are
+  // executed.
+  void AddAudio(const char* audio_buffer, int audio_buffer_size) const;
+  // Checks if the sample rate / channels changed between calls.
+  bool DidAudioPropertyChange(int sample_rate, int channel_count);
+  bool IsInitialized() const { return is_initialized_; }
+
+  // chromeos::machine_learning::mojom::SodaClient:
+  void OnStop() override;
+  void OnStart() override;
+  void OnSpeechRecognizerEvent(
+      chromeos::machine_learning::mojom::SpeechRecognizerEventPtr event)
+      override;
+
+  // Reset this client with the provided configuration, and send recognition
+  // callbacks of (text, is_final) to the given callback.
+  void Reset(chromeos::machine_learning::mojom::SodaConfigPtr soda_config,
+             base::RepeatingCallback<void(const std::string&, bool)> callback);
+
+ private:
+  // This callback is called with (text, is_final) whenever soda responds
+  // appropriately.
+  base::RepeatingCallback<void(const std::string&, bool)> callback_;
+  bool is_initialized_ = false;
+  int sample_rate_ = 0;
+  int channel_count_ = 0;
+
+  mojo::Remote<chromeos::machine_learning::mojom::SodaRecognizer>
+      soda_recognizer_;
+  mojo::Receiver<chromeos::machine_learning::mojom::SodaClient> soda_client_;
+};
+}  // namespace soda
+#endif  // CHROME_SERVICES_SPEECH_SODA_CROS_SODA_CLIENT_H_
diff --git a/chrome/services/speech/soda/proto/soda_api.proto b/chrome/services/speech/soda/proto/soda_api.proto
new file mode 100644
index 0000000..177736f
--- /dev/null
+++ b/chrome/services/speech/soda/proto/soda_api.proto
@@ -0,0 +1,203 @@
+// DO NOT CHANGE THIS FILE!
+// This proto is copied from
+// http://google3/speech/soda/chrome/extended_soda_api.proto That is the source
+// of truth, and any changes should be submitted and approved there before being
+// copied into here.
+
+syntax = "proto2";
+
+package speech.soda.api;
+
+// Optimize generated output for Lite, since it's going to be running on
+// end-user devices.
+option optimize_for = LITE_RUNTIME;
+option java_multiple_files = true;
+
+// Next ID to use: 12
+message SerializedSodaConfigMsg {
+  // Number of channels in RAW audio that will be provided to SODA.
+  optional int32 channel_count = 1;
+  // Sample rate, in Hz.
+  optional int32 sample_rate = 2;
+
+  // Maximum size of buffer to use in PipeStream. By default, is 0, which means
+  // unlimited.
+  optional int32 max_buffer_bytes = 4 [default = 0];
+
+  // If set to true, forces the audio provider to simulate realtime audio
+  // provision. This only makes sense during testing, to simulate realtime audio
+  // providing from a big chunk of audio.
+  // This slows down audio provided to SODA to a maximum of real-time, which
+  // means more accurate endpointer behavior, but is unsuitable for execution in
+  // real production environments. Set with caution!
+  optional bool simulate_realtime_testonly = 5 [default = false];
+
+  // config file location for languagepack.
+  optional string config_file_location = 3 [deprecated = true];
+
+  // API key used for call verification.
+  optional string api_key = 6;
+
+  // Directory of the language pack to use.
+  optional string language_pack_directory = 7;
+
+  enum RecognitionMode {
+    UNKNOWN = 0;
+
+    // Intended for voice input for keyboard usage.
+    IME = 1;
+
+    // Intended to caption a stream of audio.
+    CAPTION = 2;
+  }
+  // What kind of recognition to execute here. Impacts model usage.
+  optional RecognitionMode recognition_mode = 8 [default = IME];
+
+  // Whether terse_processor should force a new session after every final
+  // recognition result.
+  // This will cause the terse processor to stop processing new audio once an
+  // endpoint event is detected and wait for it to generate a final event using
+  // audio up to the endpoint. This will cause processing bursts when a new
+  // session starts.
+  optional bool reset_on_final_result = 9 [default = true];
+
+  // Whether to populate the timing_metrics field on Recognition and Endpoint
+  // events.
+  optional bool include_timing_metrics = 10 [default = true];
+
+  // Whether or not to request lang id events.
+  optional bool enable_lang_id = 11 [default = false];
+}
+
+// Next id: 5
+message TimingMetrics {
+  // Epoch time of first audio buffer of main query that is fed into ASR.
+  // This is the wall time read from the system clock when the first audio
+  // buffer is received by the terse processor.
+  optional int64 audio_start_epoch_usec = 1;
+
+  // Start time in audio time from start of SODA session.
+  // This time measures the amount of audio input into SODA.
+  optional int64 audio_start_time_usec = 2;
+
+  // Elapsed wall time usec since first frame.
+  optional int64 elapsed_wall_time_usec = 3;
+
+  // Elapsed processed audio usec from first frame after preamble.
+  optional int64 event_end_time_usec = 4;
+}
+
+// Next id: 5
+message SodaRecognitionResult {
+  // Hypothesis from recognition, in order of probability. We don't get the
+  // probability from SODA, so the only given is that the first is the "best".
+  repeated string hypothesis = 1;
+  enum ResultType {
+    UNKNOWN = 0;
+    // Partial result of a speech segment so far.
+    PARTIAL = 1;
+    // Final result for this segment.
+    FINAL = 2;
+    // Prefetch is only sent for likely query strings. This won't happen for
+    // non-query mode SODA, but we add here for completeness.
+    PREFETCH = 3;
+  }
+
+  // What kind of result set this is.
+  optional ResultType result_type = 2;
+
+  enum FinalResultEndpointReason {
+    ENDPOINT_UNKNOWN = 0;
+    // End of speech from endpointer.
+    ENDPOINT_END_OF_SPEECH = 1;
+    // End of utterance from endpointer.
+    ENDPOINT_END_OF_UTTERANCE = 2;
+    // No more audio.
+    ENDPOINT_END_OF_AUDIO = 3;
+    // Final was generated because a hotword was detected.
+    ENDPOINT_ASR_RESET_BY_HOTWORD = 4;
+    // ASR was reset via the external API.
+    ENDPOINT_ASR_RESET_EXTERNAL = 5;
+    // Final recognition result was generated due to an error in ASR.
+    ENDPOINT_ASR_ERROR = 6;
+  }
+  // If this is a final result, why was the recognition marked final.
+  optional FinalResultEndpointReason endpoint_reason = 3;
+
+  // Timing information for the event.
+  optional TimingMetrics timing_metrics = 4;
+}
+
+// Next id: 3
+message SodaEndpointEvent {
+  // What endpoint type we're referring to here.
+  enum EndpointType {
+    // A start-of-speech moment has been detected at this time. Audio currently
+    // contains speech.
+    START_OF_SPEECH = 0;
+
+    // End of speech has been detected by the endpointer, audio does not contain
+    // speech right now.
+    END_OF_SPEECH = 1;
+
+    // End of Audio due to an end-of-mic data event.
+    END_OF_AUDIO = 2;
+
+    // End of Utterance detected from the endpointer. Not used in
+    // Caption/Transcription.
+    END_OF_UTTERANCE = 3;
+
+    UNKNOWN = 4;
+  }
+
+  optional EndpointType endpoint_type = 1 [default = UNKNOWN];
+
+  // Timing information for the event.
+  optional TimingMetrics timing_metrics = 2;
+}
+
+message SodaAudioLevelInfo {
+  // Low-pass filtered RMS in range 0..1.
+  optional float rms = 1;
+
+  // Speech likelihood score from in range 0..1.
+  optional float audio_level = 2;
+
+  // Amount of audio seen from start of SODA session until an audio level event.
+  // This value is only set when audio_level is set.
+  optional int64 audio_time_usec = 3;
+}
+
+message SodaLangIdEvent {
+  // Locale, e.g. "en-us" or "af-za"
+  optional string language = 1;
+  // Equal to the internal enum from langid confidence.
+  optional int32 confidence_level = 2;
+}
+
+message SodaResponse {
+  enum SodaMessageType {
+    UNKNOWN = 0;
+    RECOGNITION = 1;
+    STOP = 2;
+    SHUTDOWN = 3;
+    START = 4;
+    ENDPOINT = 5;
+    AUDIO_LEVEL = 6;
+    LANGID = 7;
+  }
+
+  optional SodaMessageType soda_type = 1 [default = UNKNOWN];
+
+  // Set when type is RECOGNITION
+  optional SodaRecognitionResult recognition_result = 2;
+
+  // Set when type is ENDPOINT
+  optional SodaEndpointEvent endpoint_event = 3;
+
+  // Set when type is AUDIO_LEVEL
+  optional SodaAudioLevelInfo audio_level_info = 4;
+
+  // Set when type is LANGID
+  optional SodaLangIdEvent langid_event = 5;
+}
\ No newline at end of file
diff --git a/chrome/services/speech/soda/soda_async_impl.h b/chrome/services/speech/soda/soda_async_impl.h
index 6e4e5096..f80ff0f9 100644
--- a/chrome/services/speech/soda/soda_async_impl.h
+++ b/chrome/services/speech/soda/soda_async_impl.h
@@ -13,6 +13,7 @@
 extern "C" {
 
 typedef void (*RecognitionResultHandler)(const char*, const bool, void*);
+typedef void (*SerializedSodaEventHandler)(const char*, int, void*);
 
 typedef struct {
   // The channel count and sample rate of the audio stream. SODA does not
@@ -26,19 +27,36 @@
 
   // The callback that gets executed on a recognition event. It takes in a
   // char*, representing the transcribed text; a bool, representing whether the
-  // result is final or not; and a void* pointer to the SodaRecognizerImpl
-  // instance associated with the stream.
+  // result is final or not; and a void pointer to the object that is associated
+  // with the callback.
   RecognitionResultHandler callback;
 
-  // A void pointer to the SodaRecognizerImpl instance associated with the
-  // stream.
+  // A void pointer to the object that is associated with the callback.
+  // Ownership is not taken.
   void* callback_handle;
 
+  // The API key used to verify that the binary is called by Chrome.
   const char* api_key;
 } SodaConfig;
 
-// Creates and instantiates an instance of SODA.
-void* CreateSodaAsync(SodaConfig config);
+typedef struct {
+  // A ExtendedSodaConfigMsg that's been serialized as a string. Not owned.
+  const char* soda_config;
+
+  // length of char* in soda_config.
+  int soda_config_size;
+
+  // The callback that gets executed on a SODA event. It takes in a
+  // char*, which is a serialized SodaResponse proto, an int specifying the
+  // length of the char* and a void pointer to the object that is associated
+  // with the callback.
+  SerializedSodaEventHandler callback;
+
+  // A void pointer to the object that is associated with the callback.
+  void* callback_handle;
+} SerializedSodaConfig;
+
+void* CreateSoda(SerializedSodaConfig config);
 
 // Destroys the instance of SODA, called on the destruction of the SodaClient.
 void DeleteSodaAsync(void* soda_async_handle);
diff --git a/chrome/services/speech/soda/soda_client.cc b/chrome/services/speech/soda/soda_client.cc
index e1ffd08..8dfe3463 100644
--- a/chrome/services/speech/soda/soda_client.cc
+++ b/chrome/services/speech/soda/soda_client.cc
@@ -11,11 +11,13 @@
 SodaClient::SodaClient(base::FilePath library_path)
     : lib_(library_path),
       create_soda_func_(reinterpret_cast<CreateSodaFunction>(
-          lib_.GetFunctionPointer("CreateSodaAsync"))),
+          lib_.GetFunctionPointer("CreateExtendedSodaAsync"))),
       delete_soda_func_(reinterpret_cast<DeleteSodaFunction>(
-          lib_.GetFunctionPointer("DeleteSodaAsync"))),
+          lib_.GetFunctionPointer("DeleteExtendedSodaAsync"))),
       add_audio_func_(reinterpret_cast<AddAudioFunction>(
-          lib_.GetFunctionPointer("AddAudio"))),
+          lib_.GetFunctionPointer("ExtendedAddAudio"))),
+      soda_start_func_(reinterpret_cast<SodaStartFunction>(
+          lib_.GetFunctionPointer("ExtendedSodaStart"))),
       is_initialized_(false),
       sample_rate_(0),
       channel_count_(0) {
@@ -29,6 +31,7 @@
   DCHECK(create_soda_func_);
   DCHECK(delete_soda_func_);
   DCHECK(add_audio_func_);
+  DCHECK(soda_start_func_);
 }
 
 NO_SANITIZE("cfi-icall")
@@ -47,15 +50,18 @@
 }
 
 NO_SANITIZE("cfi-icall")
-void SodaClient::Reset(const SodaConfig config) {
+void SodaClient::Reset(const SerializedSodaConfig config,
+                       int sample_rate,
+                       int channel_count) {
   if (IsInitialized()) {
     delete_soda_func_(soda_async_handle_);
   }
 
   soda_async_handle_ = create_soda_func_(config);
-  sample_rate_ = config.sample_rate;
-  channel_count_ = config.channel_count;
+  sample_rate_ = sample_rate;
+  channel_count_ = channel_count;
   is_initialized_ = true;
+  soda_start_func_(soda_async_handle_);
 }
 
 }  // namespace soda
diff --git a/chrome/services/speech/soda/soda_client.h b/chrome/services/speech/soda/soda_client.h
index a36ff005..d15ae08 100644
--- a/chrome/services/speech/soda/soda_client.h
+++ b/chrome/services/speech/soda/soda_client.h
@@ -27,7 +27,9 @@
   bool DidAudioPropertyChange(int sample_rate, int channel_count);
 
   // Resets the SODA instance, initializing it with the provided config.
-  void Reset(const SodaConfig config);
+  void Reset(const SerializedSodaConfig config,
+             int sample_rate,
+             int channel_count);
 
   // Returns a flag indicating whether the client has been initialized.
   bool IsInitialized() { return is_initialized_; }
@@ -35,7 +37,7 @@
  private:
   base::ScopedNativeLibrary lib_;
 
-  typedef void* (*CreateSodaFunction)(SodaConfig);
+  typedef void* (*CreateSodaFunction)(SerializedSodaConfig);
   CreateSodaFunction create_soda_func_;
 
   typedef void (*DeleteSodaFunction)(void*);
@@ -44,6 +46,9 @@
   typedef void (*AddAudioFunction)(void*, const char*, int);
   AddAudioFunction add_audio_func_;
 
+  typedef void (*SodaStartFunction)(void*);
+  SodaStartFunction soda_start_func_;
+
   // An opaque handle to the SODA async instance.
   void* soda_async_handle_;
 
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc
index 0b0719b..20f3792a 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -11,6 +11,7 @@
 #include "base/containers/span.h"
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "chrome/services/speech/soda/proto/soda_api.pb.h"
 #include "chrome/services/speech/soda/soda_client.h"
 #include "google_apis/google_api_keys.h"
 #include "media/base/audio_buffer.h"
@@ -44,13 +45,29 @@
 // which owns the instance of SODA and their sequential destruction order
 // ensures that this callback will never be called with an invalid callback
 // handle to the SpeechRecognitionRecognizerImpl.
-void RecognitionCallback(const char* result,
-                         const bool is_final,
-                         void* callback_handle) {
+void OnSodaResponse(const char* serialized_proto,
+                    int length,
+                    void* callback_handle) {
   DCHECK(callback_handle);
-  static_cast<SpeechRecognitionRecognizerImpl*>(callback_handle)
-      ->recognition_event_callback()
-      .Run(std::string(result), is_final);
+  soda::api::SodaResponse response;
+  if (!response.ParseFromArray(serialized_proto, length)) {
+    LOG(ERROR) << "Unable to parse result from SODA.";
+    return;
+  }
+
+  if (response.soda_type() == soda::api::SodaResponse::RECOGNITION) {
+    soda::api::SodaRecognitionResult result = response.recognition_result();
+    DCHECK(result.hypothesis_size());
+    static_cast<SpeechRecognitionRecognizerImpl*>(callback_handle)
+        ->recognition_event_callback()
+        .Run(std::string(result.hypothesis(0)),
+             result.result_type() == soda::api::SodaRecognitionResult::FINAL);
+  }
+
+  if (response.soda_type() == soda::api::SodaResponse::LANGID) {
+    // TODO(crbug.com/1175357): Use the langid event to prompt users to switch
+    // languages.
+  }
 }
 
 }  // namespace
@@ -99,7 +116,7 @@
   enable_soda_ = base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption);
   if (enable_soda_) {
     DCHECK(base::PathExists(binary_path));
-    soda_client_ = std::make_unique<soda::SodaClient>(binary_path);
+    soda_client_ = std::make_unique<::soda::SodaClient>(binary_path);
   } else {
     cloud_client_ = std::make_unique<CloudSpeechRecognitionClient>(
         recognition_event_callback(),
@@ -145,6 +162,23 @@
     return;
   }
 
+  // OK, everything is verified, let's send the audio.
+  SendAudioToSpeechRecognitionServiceInternal(std::move(buffer));
+}
+
+void SpeechRecognitionRecognizerImpl::
+    SendAudioToSpeechRecognitionServiceInternal(
+        media::mojom::AudioDataS16Ptr buffer) {
+  int channel_count = buffer->channel_count;
+  int sample_rate = buffer->sample_rate;
+  size_t buffer_size = 0;
+  // Verify and calculate the buffer size.
+  if (!base::CheckMul(buffer->data.size(), sizeof(buffer->data[0]))
+           .AssignIfValid(&buffer_size)) {
+    mojo::ReportBadMessage(kInvalidAudioDataError);
+    return;
+  }
+
   if (enable_soda_) {
     DCHECK(soda_client_);
     DCHECK(base::PathExists(config_path_));
@@ -153,14 +187,23 @@
       // Initialize the SODA instance.
       auto api_key = google_apis::GetSodaAPIKey();
       std::string language_pack_directory = config_path_.AsUTF8Unsafe();
-      SodaConfig config;
-      config.channel_count = channel_count;
-      config.sample_rate = sample_rate;
-      config.language_pack_directory = language_pack_directory.c_str();
-      config.callback = RecognitionCallback;
+
+      // Initialize the SODA instance with the serialized config.
+      soda::api::SerializedSodaConfigMsg config_msg;
+      config_msg.set_channel_count(channel_count);
+      config_msg.set_sample_rate(sample_rate);
+      config_msg.set_api_key(api_key);
+      config_msg.set_language_pack_directory(language_pack_directory);
+      config_msg.set_simulate_realtime_testonly(false);
+      config_msg.set_enable_lang_id(false);
+      auto serialized = config_msg.SerializeAsString();
+
+      SerializedSodaConfig config;
+      config.soda_config = serialized.c_str();
+      config.soda_config_size = serialized.size();
+      config.callback = &OnSodaResponse;
       config.callback_handle = this;
-      config.api_key = api_key.c_str();
-      soda_client_->Reset(config);
+      soda_client_->Reset(config, sample_rate, channel_count);
     }
 
     soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h
index 3fc84e9..187958a 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.h
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -54,6 +54,14 @@
     return recognition_event_callback_;
   }
 
+ protected:
+  virtual void SendAudioToSpeechRecognitionServiceInternal(
+      media::mojom::AudioDataS16Ptr buffer);
+
+  // Return the transcribed audio from the recognition event back to the caller
+  // via the recognition event client.
+  void OnRecognitionEvent(const std::string& result, const bool is_final);
+
  private:
   // Convert the audio buffer into the appropriate format and feed the raw audio
   // into the speech recognition instance.
@@ -64,10 +72,6 @@
 
   void AudioReceivedAfterBubbleClosed(base::TimeDelta duration) final;
 
-  // Return the transcribed audio from the recognition event back to the caller
-  // via the recognition event client.
-  void OnRecognitionEvent(const std::string& result, const bool is_final);
-
   void RecordDuration();
 
   // The remote endpoint for the mojo pipe used to return transcribed audio from
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 84bd135d..d10e11d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -45,6 +45,10 @@
 import("//ui/webui/webui_features.gni")
 import("//v8/gni/v8.gni")
 
+if (is_chromeos) {
+  import("//build/config/chromeos/rules.gni")
+}
+
 assert(!is_ios, "Chromium/iOS shouldn't use anything in //chrome")
 
 if (is_android) {
@@ -1411,7 +1415,7 @@
       "../browser/ui/autofill/payments/card_unmask_prompt_view_tester.h",
       "../browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc",
       "../browser/ui/autofill/payments/save_upi_bubble_controller_impl_browsertest.cc",
-      "../browser/ui/autofill/save_address_profile_bubble_controller_browsertest.cc",
+      "../browser/ui/autofill/save_address_profile_bubble_controller_impl_browsertest.cc",
       "../browser/ui/blocked_content/popup_opener_tab_helper_browsertest.cc",
       "../browser/ui/blocked_content/popup_tracker_browsertest.cc",
       "../browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc",
@@ -2189,6 +2193,8 @@
         ]
 
         deps += [ "//chrome/browser/error_reporting:test_support" ]
+      } else {
+        sources += [ "../browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc" ]
       }
 
       deps += [
@@ -2432,6 +2438,8 @@
         "../browser/ash/app_mode/kiosk_crash_restore_browsertest.cc",
         "../browser/ash/app_mode/web_app/web_kiosk_app_data_browsertest.cc",
         "../browser/ash/profiles/profile_helper_browsertest.cc",
+        "../browser/ash/system/device_disabling_browsertest.cc",
+        "../browser/ash/system/tray_accessibility_browsertest.cc",
         "../browser/chrome_main_browsertest.cc",
         "../browser/chromeos/apps/apk_web_app_installer_browsertest.cc",
         "../browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc",
@@ -2741,8 +2749,6 @@
         "../browser/chromeos/scoped_test_system_nss_key_slot_mixin.h",
         "../browser/chromeos/shutdown_policy_browsertest.cc",
         "../browser/chromeos/startup_settings_cache_browsertest.cc",
-        "../browser/chromeos/system/device_disabling_browsertest.cc",
-        "../browser/chromeos/system/tray_accessibility_browsertest.cc",
         "../browser/chromeos/web_applications/camera_app_integration_browsertest.cc",
         "../browser/chromeos/web_applications/diagnostics_app_integration_browsertest.cc",
         "../browser/chromeos/web_applications/eche_app_integration_browsertest.cc",
@@ -3336,42 +3342,70 @@
 
 # New target that will replace telemetry_perf_tests when testing
 # is done.
-script_test("performance_test_suite") {
-  script = "//testing/scripts/run_performance_tests.py"
-  args = [ "../../tools/perf/run_benchmark" ]
+template("performance_test_suite_template") {
+  forward_variables_from(invoker, [ "override_board" ])
+  script_test(target_name) {
+    script = "//testing/scripts/run_performance_tests.py"
+    if (is_chromeos_device) {
+      if (defined(override_board)) {
+        args = [ "bin/cros_update_wrapper_${override_board} -- ../../tools/perf/run_benchmark" ]
+      } else {
+        args = [ "bin/cros_update_wrapper -- ../../tools/perf/run_benchmark" ]
+      }
+    } else {
+      args = [ "../../tools/perf/run_benchmark" ]
+    }
 
-  data_deps = [
-    "//base:base_perftests",
-    "//chrome/test:telemetry_perf_tests",
-    "//components:components_perftests",
-    "//components/tracing:tracing_perftests",
-    "//gpu:command_buffer_perftests",
-    "//gpu:gpu_perftests",
-    "//media:media_perftests",
-    "//testing:run_perf_test",
-  ]
-
-  if (build_dawn_tests && !is_chromeos_lacros) {
-    data_deps += [ "//third_party/dawn/src/tests:dawn_perf_tests" ]
-  }
-
-  if (!is_chromeos_lacros) {
-    data_deps += [ "//third_party/angle/src/tests:angle_perftests" ]
-  }
-
-  if (!is_android && !is_fuchsia) {
-    data_deps += [
-      "//chrome/test:load_library_perf_tests",
-      "//ui/views:views_perftests",
+    data_deps = [
+      "//base:base_perftests",
+      "//chrome/test:telemetry_perf_tests",
+      "//components:components_perftests",
+      "//components/tracing:tracing_perftests",
+      "//gpu:command_buffer_perftests",
+      "//gpu:gpu_perftests",
+      "//media:media_perftests",
+      "//testing:run_perf_test",
     ]
-  }
 
-  if (!is_android) {
-    data_deps += [ "//chrome/test:performance_browser_tests" ]
-  }
+    if (build_dawn_tests && !is_chromeos_lacros) {
+      data_deps += [ "//third_party/dawn/src/tests:dawn_perf_tests" ]
+    }
 
-  if (!is_ios && !is_fuchsia && !is_android) {
-    data_deps += [ "//net:net_perftests" ]
+    if (!is_chromeos_lacros) {
+      data_deps += [ "//third_party/angle/src/tests:angle_perftests" ]
+    }
+
+    if (!is_android && !is_fuchsia) {
+      data_deps += [
+        "//chrome/test:load_library_perf_tests",
+        "//ui/views:views_perftests",
+      ]
+    }
+
+    if (!is_android) {
+      data_deps += [ "//chrome/test:performance_browser_tests" ]
+    }
+
+    if (!is_ios && !is_fuchsia && !is_android) {
+      data_deps += [ "//net:net_perftests" ]
+    }
+  }
+}
+
+performance_test_suite_template("performance_test_suite") {
+}
+
+# Generate performance_test_suite_${board} targets.
+# These set --board to ${board} instead of deriving it from the target.
+# This is useful for Lacros, where the generic build is deployed to
+# many different targets.
+if (is_chromeos_device && cros_boards != "") {
+  foreach(_board, string_split(cros_boards, ":")) {
+    _board_underscore = string_replace(_board, "-", "_")
+    performance_test_suite_template(
+        "performance_test_suite_${_board_underscore}") {
+      override_board = _board
+    }
   }
 }
 
@@ -4067,6 +4101,13 @@
       "../browser/webauthn/chrome_authenticator_request_delegate_unittest.cc",
     ]
   }
+
+  if (is_chromeos_ash) {
+    sources += [
+      "../browser/speech/cros_speech_recognition_service_factory_unittest.cc",
+    ]
+  }
+
   if (is_chromeos_ash) {
     sources += [
       "../../chromeos/memory/userspace_swap/userspace_swap.cc",
@@ -4869,7 +4910,7 @@
       "../browser/sharing/sms/sms_remote_fetcher_unittest.cc",
       "../browser/ui/autofill/payments/local_card_migration_bubble_controller_impl_unittest.cc",
       "../browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc",
-      "../browser/ui/autofill/save_address_profile_bubble_controller_unittest.cc",
+      "../browser/ui/autofill/save_address_profile_bubble_controller_impl_unittest.cc",
       "../browser/ui/bluetooth/bluetooth_chooser_controller_unittest.cc",
       "../browser/ui/bluetooth/bluetooth_scanning_prompt_controller_unittest.cc",
       "../browser/ui/media_router/cast_modes_with_media_sources_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
index fbff5e72..924e367 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
@@ -119,6 +119,13 @@
     }
 
     /**
+     * Removes an account with the given account email.
+     */
+    public void removeAccount(String accountEmail) {
+        mFakeAccountManagerFacade.removeAccount(AccountUtils.createAccountFromName(accountEmail));
+    }
+
+    /**
      * Waits for the AccountTrackerService to seed system accounts.
      */
     public void waitForSeeding() {
@@ -144,8 +151,8 @@
      *
      * This method invokes native code. It shouldn't be called in a Robolectric test.
      */
-    public void removeAccountAndWaitForSeeding(String accountName) {
-        mFakeAccountManagerFacade.removeAccount(AccountUtils.createAccountFromName(accountName));
+    public void removeAccountAndWaitForSeeding(String accountEmail) {
+        removeAccount(accountEmail);
         waitForSeeding();
     }
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index fd05abd..93092907 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -5104,7 +5104,8 @@
   ChromeDriverBaseTestWithWebServer.GlobalSetUp()
 
   runner = unittest.TextTestRunner(
-      stream=sys.stdout, descriptions=False, verbosity=2)
+      stream=sys.stdout, descriptions=False, verbosity=2,
+      resultclass=unittest_util.AddSuccessTextTestResult)
   result = runner.run(test_suite)
   results = [result]
 
@@ -5128,4 +5129,5 @@
   if options.isolated_script_test_output:
     util.WriteResultToJSONFile(test_suites, results,
                                options.isolated_script_test_output)
+  util.TryUploadingResultToResultSink(results)
   sys.exit(len(results[-1].failures) + len(results[-1].errors))
diff --git a/chrome/test/chromedriver/test/unittest_util.py b/chrome/test/chromedriver/test/unittest_util.py
index 52363c2..6a3fcd1c 100644
--- a/chrome/test/chromedriver/test/unittest_util.py
+++ b/chrome/test/chromedriver/test/unittest_util.py
@@ -128,3 +128,15 @@
     else:
       tests += [test]
   return tests
+
+
+class AddSuccessTextTestResult(unittest.runner.TextTestResult):
+
+  def __init__(self, stream, descriptions, verbosity):
+    super(AddSuccessTextTestResult, self).__init__(
+            stream, descriptions, verbosity)
+    self.successes = []
+
+  def addSuccess(self, test):
+    super(AddSuccessTextTestResult, self).addSuccess(test)
+    self.successes.append(test)
diff --git a/chrome/test/chromedriver/util.py b/chrome/test/chromedriver/util.py
index 32f3c578..16875fd 100644
--- a/chrome/test/chromedriver/util.py
+++ b/chrome/test/chromedriver/util.py
@@ -5,11 +5,13 @@
 """Generic utilities for all python scripts."""
 
 import atexit
+import base64
 import httplib
 import json
 import os
 import platform
 import random
+import requests
 import signal
 import socket
 import stat
@@ -334,3 +336,59 @@
   with open(json_path, 'w') as script_out_file:
     json.dump(output, script_out_file)
     script_out_file.write('\n')
+
+
+def TryUploadingResultToResultSink(results):
+
+  def parse(result):
+    test_results = []
+    for test_case in result.successes:
+      test_results.append({
+          'testId': test_case.id(),
+          'expected': True,
+          'status': 'PASS',
+      })
+
+    for (test_case, stack_trace) in result.failures + result.errors:
+      test_results.append({
+          'testId': test_case.id(),
+          'expected': False,
+          'status': 'FAIL',
+          # Uses <text-artifact> tag to embed the artifact content
+          # in summaryHtml.
+          'summaryHtml': '<p><text-artifact artifact-id="stack_trace"></p>',
+          # A map of artifacts. The keys are artifact ids which uniquely
+          # identify an artifact within the test result.
+          'artifacts': {
+               'stack_trace': {
+                    'contents': base64.b64encode(stack_trace),
+               },
+          },
+      })
+    return test_results
+
+  def getResultSinkTestResults(results):
+    test_results = []
+    for r in results:
+        test_results.extend(parse(r))
+    return test_results
+
+  try:
+    with open(os.environ['LUCI_CONTEXT']) as f:
+      sink = json.load(f)['result_sink']
+  except KeyError:
+    return
+
+  test_results = getResultSinkTestResults(results)
+  # Uploads all test results at once.
+  res = requests.post(
+    url='http://%s/prpc/luci.resultsink.v1.Sink/ReportTestResults' % sink['address'],
+    headers={
+      'Content-Type': 'application/json',
+      'Accept': 'application/json',
+      'Authorization': 'ResultSink %s' % sink['auth_token'],
+    },
+    data=json.dumps({'testResults': test_results})
+  )
+  res.raise_for_status()
+
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/file.css b/chrome/test/data/extensions/api_test/scripting/remove_css/file.css
new file mode 100644
index 0000000..ff8fd1b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/file.css
@@ -0,0 +1,7 @@
+/* Copyright 2021 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. */
+
+#main {
+  color: green !important;
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/frame1.html b/chrome/test/data/extensions/api_test/scripting/remove_css/frame1.html
new file mode 100644
index 0000000..e366406
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/frame1.html
@@ -0,0 +1,11 @@
+<html>
+  <style>
+    #main {
+      color: red;
+    }
+  </style>
+  <div id="main">
+    Hello.
+  </div>
+  <iframe id="frame2" src="frame2.html"></iframe>
+</html>
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/frame2.html b/chrome/test/data/extensions/api_test/scripting/remove_css/frame2.html
new file mode 100644
index 0000000..c6ab9ba
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/frame2.html
@@ -0,0 +1,11 @@
+<html>
+  <style>
+    #main {
+      color: red;
+    }
+  </style>
+  <div id="main">
+    Hello.
+  </div>
+  <iframe id="frame3" src="frame3.html"></iframe>
+</html>
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/frame3.html b/chrome/test/data/extensions/api_test/scripting/remove_css/frame3.html
new file mode 100644
index 0000000..70d813f7
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/frame3.html
@@ -0,0 +1,11 @@
+<html>
+  <style>
+    #main {
+      color: red;
+    }
+  </style>
+  <div id="main">
+    Hello.
+  </div>
+  <iframe id="frame4" srcdoc="<style>#main{color:red}</style><div id='main'>Hello.</div>"></iframe>
+</html>
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/manifest.json b/chrome/test/data/extensions/api_test/scripting/remove_css/manifest.json
new file mode 100644
index 0000000..343d51f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/manifest.json
@@ -0,0 +1,9 @@
+{
+  "version": "1.0.0.0",
+  "manifest_version": 3,
+  "name": "removeCSS test",
+  "description": "Test extension API: scripting.removeCSS",
+  "background": { "service_worker": "worker.js" },
+  "permissions": ["scripting", "webNavigation"],
+  "host_permissions": ["http://example.com/"]
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/other.css b/chrome/test/data/extensions/api_test/scripting/remove_css/other.css
new file mode 100644
index 0000000..ff8fd1b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/other.css
@@ -0,0 +1,7 @@
+/* Copyright 2021 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. */
+
+#main {
+  color: green !important;
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/test.html b/chrome/test/data/extensions/api_test/scripting/remove_css/test.html
new file mode 100644
index 0000000..6a501cf
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/test.html
@@ -0,0 +1,11 @@
+<html>
+  <style>
+    #main {
+      color: red;
+    }
+  </style>
+  <div id="main">
+    Hello.
+  </div>
+  <iframe id="frame1" src="frame1.html"></iframe>
+</html>
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js b/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js
new file mode 100644
index 0000000..16986080
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js
@@ -0,0 +1,317 @@
+// Copyright 2021 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 strict';
+
+// The original color of the page that we're injecting into.
+const ORIGINAL_COLOR = 'rgb(255, 0, 0)';  // red
+
+// The color we inject into the page (first).
+const INJECTED_COLOR = 'rgb(0, 128, 0)';  // green
+
+// A secondary color we inject into the page (so that we can differentiate
+// between the original and first injected color).
+const INJECTED_COLOR2 = 'rgb(255, 255, 0)';  // yellow
+
+// CSS to inject, corresopnding to `INJECTED_COLOR`.
+const CSS = '#main { color: green !important; }';
+// CSS to inject, corresponding to `INJECTED_COLOR2`.
+const CSS2 = CSS.replace('green', 'yellow');
+// A file to inject. This also sets the color to `INJECTED_COLOR`.
+const FILE = '/file.css';
+
+// Returns the frame IDs from the tab with the given `tabId`.
+async function getFrameIds(tabId) {
+  // TODO(devlin: Promise-ify webNavigation.
+  let frames = await new Promise(resolve => {
+      chrome.webNavigation.getAllFrames({tabId}, resolve);
+  });
+
+  // Sort frames by frameId.
+  let sortedFrames = frames.sort(
+      (a, b) => a.frameId < b.frameId ? -1 : a.frameId > b.frameId ? 1 : 0);
+
+  // Validate the frames - there should be 5 total, and the main frame should
+  // be first.
+  chrome.test.assertEq(5, sortedFrames.length);
+  chrome.test.assertEq(sortedFrames[0].frameId, 0 /*main frame id*/);
+  chrome.test.assertTrue(
+      sortedFrames[1].frameId > 0 /* first non-main-frame id */);
+
+  // Return the array of frame IDs.
+  return sortedFrames.map(frame => frame.frameId);
+}
+
+// Returns the current color of the frame with `frameId` in the tab with
+// `tabId`.
+async function getCurrentColor(tabId, frameId) {
+  return await new Promise((resolve) => {
+    chrome.scripting.executeScript(
+        {
+          target: {tabId, frameIds: [frameId]},
+          function: () => {
+            const element = document.getElementById('main');
+            const style = getComputedStyle(element);
+            return style.getPropertyValue('color');
+          },
+        },
+        (scriptResults) => {
+          chrome.test.assertEq(1, scriptResults.length)
+          resolve(scriptResults[0].result);
+        });
+    });
+}
+
+let tabId = -1;
+let frameIds = [];
+
+// `expectedColorsForFrames` holds a snapshot of expected values of the CSS
+// colors being inserted/removed, for each of the frame ids. The array is
+// sorted in the order of the frame IDs, such that the color at
+// `expectedColorsForFrames[0] corresponds to the color for the frame with
+// id `frameIds[0]`. Values get validated at the completion of the each test
+// below.
+//
+// Each frame is a child of the frame preceding it. Frames 0 through 3 are
+// <iframe src="..."> while frame 4 is <iframe srcdoc="..."> (about:srcdoc).
+let expectedColorsForFrames = [
+  ORIGINAL_COLOR, ORIGINAL_COLOR, ORIGINAL_COLOR, ORIGINAL_COLOR,
+  ORIGINAL_COLOR
+];
+
+// Updates the expected state in `expectedColorsForFrames`. Undefined values in
+// `delta` correspond to "no change" in `expectedColorsForFrames`.
+function updateExpectedState(delta) {
+  Object.assign(expectedColorsForFrames, delta);
+}
+
+// Validates that the colors are as expected for each frame.
+async function checkColors() {
+  for (let i = 0; i < frameIds.length; ++i) {
+    const frameId = frameIds[i];
+    let color = await getCurrentColor(tabId, frameId);
+    chrome.test.assertEq(
+        expectedColorsForFrames[i], color,
+        `Improper color value for frame: ${frameId}`);
+  }
+}
+
+// Helper function to insert CSS.
+// TODO(devlin): Remove when chrome.scripting is promisified.
+async function insertCSS(args) {
+  await new Promise(resolve => {
+    chrome.scripting.insertCSS(args, () => {
+      chrome.test.assertNoLastError();
+      resolve();
+    });
+  });
+}
+
+// Helper function to remove CSS.
+// TODO(devlin): Remove when chrome.scripting is promisified.
+async function removeCSS(args) {
+  await new Promise(resolve => {
+    chrome.scripting.removeCSS(args, () => {
+      chrome.test.assertNoLastError();
+      resolve();
+    });
+  });
+}
+
+// Loads `url` in a new tab, waits for it to finish loading, and returns the
+// tabId of the newly-created tab.
+// TODO(https://crbug.com/824647): Update this to use
+// test_resources/tabs_util.js when extension service workers support
+// modules.
+async function createTab(url) {
+  return new Promise(resolve => {
+    // Wait for `url` to finish loading.
+    chrome.tabs.onUpdated.addListener(
+        async function listener(updatedTabId, {status}) {
+      if (status != 'complete')
+        return;
+      chrome.tabs.onUpdated.removeListener(listener);
+
+      resolve(updatedTabId);
+    });
+
+    chrome.tabs.create({url});
+  });
+}
+
+chrome.test.runTests([
+  async function loadTab() {
+    const config = await new Promise(resolve => {
+      chrome.test.getConfig(resolve);
+    });
+    const testUrl = `http://example.com:${config.testServer.port}` +
+        '/extensions/api_test/scripting/remove_css/test.html';
+    tabId = await createTab(testUrl);
+    frameIds = await getFrameIds(tabId);
+
+    chrome.test.succeed();
+  },
+  async function insertCSSShouldSucceed() {
+    // Insert CSS into every frame.
+    await insertCSS({target: {tabId, allFrames: true}, css: CSS});
+    updateExpectedState(
+        [INJECTED_COLOR, INJECTED_COLOR, INJECTED_COLOR,
+         INJECTED_COLOR, INJECTED_COLOR]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSShouldSucceed() {
+    // When no frame ID is specified, the CSS is removed from the top frame
+    // Others should be unaffected (and keep the injected color).
+    await removeCSS({target: {tabId}, css: CSS});
+    updateExpectedState([ORIGINAL_COLOR, , , , , ]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithDifferentCodeShouldDoNothing() {
+    // If the specified code differs by even one character, it does not
+    // match any inserted CSS and therefore nothing is removed.
+    const slightlyOffCSS = CSS + ' ';
+    // TODO(devlin): We don't currently return an error if the CSS to remove
+    // did not match an inserted stylesheet. We could, which would make it
+    // easier for developers to catch mistakes.
+    await removeCSS({target: {tabId, allFrames: true}, css: slightlyOffCSS});
+    // Note: no change in expected state.
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithDifferentCSSOriginShouldDoNothing() {
+    // If only the CSS origin differs, nothing is removed.
+    await removeCSS({target: {tabId, frameIds}, css: CSS, origin: 'USER'});
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithFrameIdShouldSucceed() {
+    // When a frame ID is specified, the CSS is removed only from the given
+    // frame.
+    await removeCSS(
+        {target: {tabId, frameIds: [frameIds[1]]}, css: CSS});
+    updateExpectedState([ , ORIGINAL_COLOR, , , , ]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithAllFramesShouldSucceed() {
+    // When "allFrames" is set to true, the CSS is removed from all
+    // frames.
+    await removeCSS({target: {tabId, allFrames: true}, css: CSS});
+    updateExpectedState([ORIGINAL_COLOR, ORIGINAL_COLOR, ORIGINAL_COLOR,
+                         ORIGINAL_COLOR, ORIGINAL_COLOR]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function insertCSSWithFileShouldSucceed() {
+    // Insert some CSS using a file (to then be removed).
+    await insertCSS({target: {tabId, allFrames: true}, files: [FILE]});
+    updateExpectedState([INJECTED_COLOR, INJECTED_COLOR, INJECTED_COLOR,
+                         INJECTED_COLOR, INJECTED_COLOR]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithFileShouldSucceed() {
+    // When no frame ID is specified, the CSS is removed from the top frame.
+    await removeCSS({target: {tabId}, files: [FILE]});
+    updateExpectedState([ORIGINAL_COLOR, , , , , ]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithDifferentFileShouldDoNothing() {
+    // The CSS is not removed when passing a different file (even though the
+    // contents of /file.css and /other.css are identical).
+    await removeCSS(
+        {target: {tabId, allFrames: true}, files: ['/other.css']});
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function insertCSSWithDuplicateCodeShouldSucceed() {
+    // Start by inserting the second CSS (which is a different color) into the
+    // top frame.
+    await insertCSS({target: {tabId}, css: CSS2});
+    updateExpectedState([INJECTED_COLOR2, , , , , ]);
+    await checkColors();
+    // Then, re-insert the first CSS. The top frame should be updated.
+    await insertCSS({target: {tabId}, css: CSS});
+    updateExpectedState([INJECTED_COLOR, , , , , ]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function removeCSSWithDuplicateCodeShouldSucceed() {
+    // Remove the first CSS. The second-inserted CSS should take effect again.
+    await removeCSS({target: {tabId}, css: CSS});
+    updateExpectedState([INJECTED_COLOR2, , , , , ]);
+    await checkColors();
+
+    // Remove the second CSS. The color should go back to the original color of
+    // the frame.
+    await removeCSS({target: {tabId}, css: CSS2});
+    updateExpectedState([ORIGINAL_COLOR, , , , , ]);
+    await checkColors();
+    chrome.test.succeed();
+  },
+  async function noSuchTab() {
+    const nonExistentTabId = 99999;
+    // NOTE(devlin): We can't use a fancy `await` here, because the lastError
+    // won't be properly set. This will work better with true promise support,
+    // where this could be wrapped in an e.g. expectThrows().
+    chrome.scripting.removeCSS(
+        {
+          target: {
+            tabId: nonExistentTabId,
+          },
+          css: CSS,
+        },
+        async () => {
+          chrome.test.assertLastError(`No tab with id: ${nonExistentTabId}`);
+          await checkColors();
+          chrome.test.succeed();
+        });
+  },
+  async function noSuchFile() {
+    const noSuchFile = 'no_such_file.js';
+    chrome.scripting.removeCSS(
+        {
+          target: {tabId},
+          files: [noSuchFile],
+        },
+        async () => {
+          // Edge case: When removing inserted files, we don't actually read
+          // the file content (because it's unnecessary, and would be wasteful).
+          // We also don't fire an error when there was no matching CSS inserted
+          // (see "removeCSSWithDifferentCodeShouldDoNothing()" test case). This
+          // combines to mean that even though there's no such file here, we
+          // don't actually fire an error. This will be fixed if/when we return
+          // an error for removing a non-existent stylesheet.
+          chrome.test.assertNoLastError();
+          await checkColors();
+          chrome.test.succeed();
+        });
+  },
+  async function disallowedPermission() {
+    const config = await new Promise(resolve => {
+      chrome.test.getConfig(resolve);
+    });
+    const testUrl = `http://google.com:${config.testServer.port}` +
+        '/extensions/api_test/scripting/remove_css/test.html';
+    tabId = await createTab(testUrl);
+    // Note: We don't test the expected colors here, because that relies on the
+    // extension having access to the host (in order to inject the script to
+    // retrieve the colors), which it doesn't have here.
+    chrome.scripting.removeCSS(
+        {
+          target: {tabId},
+          css: CSS,
+        },
+        async () => {
+          chrome.test.assertLastError(
+              'Cannot access contents of the page. ' +
+              'Extension manifest must request permission to ' +
+              'access the respective host.');
+          chrome.test.succeed();
+        });
+  },
+]);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 3ac4541b..8d41235 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -6812,6 +6812,11 @@
             {
               "url": "https://xyz.example",
               "create_desktop_shortcut": true
+            },
+            {
+              "url": "https://foo.example",
+              "default_launch_container": "window",
+              "fallback_app_name": "Foo Example App"
             }
           ]
         },
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 387bf22..2989744 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -351,6 +351,8 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_bluetooth.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_receive_manager.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_quick_unlock_private.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_user_action_recorder.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/google_assistant_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/guest_os_shared_usb_devices_test.m.js",
@@ -390,6 +392,7 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/lock_screen_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_search_page_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/people_page_change_picture_test.m.js",
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_store_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_store_test.js
index 1d0a88a..625b4276 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_store_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_store_test.js
@@ -33,11 +33,11 @@
 
   test('one emoji in recently used', () => {
     store.bumpEmoji(TEST_EMOJI.grin.string);
-    assertDeepEquals([TEST_EMOJI.grin.codepoints], store.data);
+    assertDeepEquals([TEST_EMOJI.grin.string], store.data);
 
     store.bumpEmoji(TEST_EMOJI.grin.string);
     assertDeepEquals(
-        [TEST_EMOJI.grin.codepoints], store.data,
+        [TEST_EMOJI.grin.string], store.data,
         'clicking an existing emoji should not duplicate it');
   });
 
@@ -45,11 +45,11 @@
     store.bumpEmoji(TEST_EMOJI.grin.string);
     store.bumpEmoji(TEST_EMOJI.waving.string);
     assertDeepEquals(
-        [TEST_EMOJI.waving.codepoints, TEST_EMOJI.grin.codepoints], store.data);
+        [TEST_EMOJI.waving.string, TEST_EMOJI.grin.string], store.data);
 
     store.bumpEmoji(TEST_EMOJI.grin.string);
     assertDeepEquals(
-        [TEST_EMOJI.grin.codepoints, TEST_EMOJI.waving.codepoints], store.data,
+        [TEST_EMOJI.grin.string, TEST_EMOJI.waving.string], store.data,
         'clicking an existing emoji should move it to the front');
   });
 });
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
index 4a662c23..f6ef600 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
@@ -10,28 +10,86 @@
 
 // #import {CellularSetupPageName} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
 // #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertTrue} from '../../../chai_assert.js';
+// #import {assertTrue, assertFalse} from '../../../chai_assert.js';
 // #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
+// #import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.m.js';
+// #import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+// #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+// #import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
 // clang-format on
 
 suite('CrComponentsCellularSetupTest', function() {
   let cellularSetupPage;
+  let eSimManagerRemote;
+  let networkConfigRemote;
+
   setup(function() {
+    eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
+    cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
+
+    networkConfigRemote = new FakeNetworkConfig();
+    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote;
+  });
+
+  async function flushAsync() {
+    Polymer.dom.flush();
+    // Use setTimeout to wait for the next macrotask.
+    return new Promise(resolve => setTimeout(resolve));
+  }
+
+  function init() {
     cellularSetupPage = document.createElement('cellular-setup');
     cellularSetupPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
     document.body.appendChild(cellularSetupPage);
     Polymer.dom.flush();
+  }
+
+  test('Show pSim flow ui if now esim network is available', async function() {
+    const mojom = chromeos.networkConfig.mojom;
+    networkConfigRemote.setDeviceStateForTest({
+      type: mojom.NetworkType.kCellular,
+      deviceState: chromeos.networkConfig.mojom.DeviceStateType.kEnabled,
+      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
+    });
+    eSimManagerRemote.addEuiccForTest(2);
+    await flushAsync();
+    init();
+
+    await flushAsync();
+    const eSimFlow = cellularSetupPage.$$('esim-flow-ui');
+    assertFalse(!!eSimFlow);
+    const pSimFlow = cellularSetupPage.$$('psim-flow-ui');
+    assertTrue(!!pSimFlow);
   });
 
-  test('Base test', function() {
-    const ironPage = cellularSetupPage.$$('iron-pages');
-    assertTrue(!!ironPage);
+  test('Show eSIM flow ui if no pSIM networks is available', async function() {
+    const mojom = chromeos.networkConfig.mojom;
+    networkConfigRemote.setDeviceStateForTest({
+      type: mojom.NetworkType.kCellular,
+      deviceState: chromeos.networkConfig.mojom.DeviceStateType.kEnabled,
+      simInfos: [{eid: '2221111112222', slot_id: 0, iccid: '1111111111111111'}],
+    });
+    eSimManagerRemote.addEuiccForTest(2);
+    await flushAsync();
+
+    init();
+    await flushAsync();
+    const pSimFlow = cellularSetupPage.$$('psim-flow-ui');
+    assertFalse(!!pSimFlow);
+    const eSimFlow = cellularSetupPage.$$('esim-flow-ui');
+    assertTrue(!!eSimFlow);
   });
 
-  test('Page selection change', function() {
-    assertTrue(
-        cellularSetupPage.currentPageName ===
-        cellularSetup.CellularSetupPageName.SETUP_FLOW_SELECTION);
+  test('Page selection change', async function() {
+    const mojom = chromeos.networkConfig.mojom;
+    networkConfigRemote.setDeviceStateForTest({
+      type: mojom.NetworkType.kCellular,
+      deviceState: chromeos.networkConfig.mojom.DeviceStateType.kEnabled,
+      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
+    });
+    eSimManagerRemote.addEuiccForTest(2);
+    await flushAsync();
+    init();
 
     const selectionFlow = cellularSetupPage.$$('setup-selection-flow');
     assertTrue(!!selectionFlow);
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
index 4d577da9..4cf049f 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
@@ -71,6 +71,8 @@
   ['ButtonBar', 'cellular_setup/button_bar_test.js',[]],
   ['CellularSetup', 'cellular_setup/cellular_setup_test.js', [
     './cellular_setup/fake_cellular_setup_delegate.js',
+    './cellular_setup/fake_esim_manager_remote.js',
+    '../../chromeos/fake_network_config_mojom.js',
   ]],
   ['EsimFlowUi', 'cellular_setup/esim_flow_ui_test.js',[
     './cellular_setup/fake_cellular_setup_delegate.js',
diff --git a/chrome/test/data/webui/histograms/histograms_internals_ui_browsertest.js b/chrome/test/data/webui/histograms/histograms_internals_ui_browsertest.js
index af60a639..24954a2 100644
--- a/chrome/test/data/webui/histograms/histograms_internals_ui_browsertest.js
+++ b/chrome/test/data/webui/histograms/histograms_internals_ui_browsertest.js
@@ -32,31 +32,39 @@
 };
 
 TEST_F('HistogramsInternalsUIBrowserTest', 'RefreshHistograms', function() {
-  test('check refresh button replaces existing histograms', function() {
-    return cr.sendWithPromise('requestHistograms', '')
-        .then(addHistograms)
-        .finally(() => {
+  test(
+      'check refresh button replaces existing histograms', function() {
+        const whenRefreshed = new Promise((resolve, reject) => {
+          document.querySelector('#histograms')
+              .addEventListener('histograms-updated-for-test', resolve);
+        });
+        document.querySelector('#refresh').click();
+        return whenRefreshed.then(() => {
           const histogramHeader = 'Histogram: HTMLOut recorded 5 samples';
           assertEquals(
               document.body.textContent.indexOf(histogramHeader),
               document.body.textContent.lastIndexOf(histogramHeader),
               'refresh should replace existing histograms');
         });
-  });
+      });
 
   mocha.run();
 });
 
 TEST_F('HistogramsInternalsUIBrowserTest', 'NoDummyHistograms', function() {
-  test('check no dummy histogram is present', function() {
-    return cr.sendWithPromise('requestHistograms', '')
-        .then(addHistograms)
-        .finally(() => {
+  test(
+      'check no dummy histogram is present', function() {
+        const whenRefreshed = new Promise((resolve, reject) => {
+          document.querySelector('#histograms')
+              .addEventListener('histograms-updated-for-test', resolve);
+        });
+        document.querySelector('#refresh').click();
+        return whenRefreshed.then(() => {
           document.querySelectorAll('h4').forEach(header => {
             assertNotEquals(header.textContent, '');
           });
         });
-  });
+      });
 
   mocha.run();
-});
\ No newline at end of file
+});
diff --git a/chrome/test/data/webui/invalidations/about_invalidations_browsertest.js b/chrome/test/data/webui/invalidations/about_invalidations_browsertest.js
index 20504df..19b4ec5a 100644
--- a/chrome/test/data/webui/invalidations/about_invalidations_browsertest.js
+++ b/chrome/test/data/webui/invalidations/about_invalidations_browsertest.js
@@ -25,6 +25,9 @@
     '//chrome/test/data/webui/mocha_adapter.js',
   ],
 
+  /** @override */
+  isAsync: true,
+
   suiteName: 'invalidations_test',
 
   /** @param {string} testName The name of the test to run. */
diff --git a/chrome/test/data/webui/invalidations/invalidations_test.js b/chrome/test/data/webui/invalidations/invalidations_test.js
index c02ef2b..3f28f6a 100644
--- a/chrome/test/data/webui/invalidations/invalidations_test.js
+++ b/chrome/test/data/webui/invalidations/invalidations_test.js
@@ -62,13 +62,15 @@
       {name: 'EXTENSIONS', source: 1004, totalCount: 0},
       {name: 'FAVICON_IMAGE', source: 1004, totalCount: 0}
     ];
-    var pattern1 = ['Fake', '1004', 'EXTENSIONS', '0', '0', '', '', ''];
-    var pattern2 = ['Fake', '1004', 'FAVICON_IMAGE', '0', '0', '', '', ''];
+    const registrarName = 'Fake';
+    var pattern1 = [registrarName, '1004', 'EXTENSIONS', '0', '0', '', '', ''];
+    var pattern2 =
+        [registrarName, '1004', 'FAVICON_IMAGE', '0', '0', '', '', ''];
     // Register two objects ID with 'Fake' registrar
-    webUIListenerCallback('ids-updated', newDataType);
+    webUIListenerCallback('ids-updated', registrarName, newDataType);
     // Disable the Extensions ObjectId by only sending FAVICON_IMAGE
     newDataType = [{name: 'FAVICON_IMAGE', source: 1004}];
-    webUIListenerCallback('ids-updated', newDataType);
+    webUIListenerCallback('ids-updated', registrarName, newDataType);
 
     // Test that the two patterns are contained in the table.
     var oidTable = $('objectsid-table-container');
@@ -116,7 +118,7 @@
 
   // Test that an object showing internal state is correctly displayed.
   test(invalidations_test.TestNames.UpdateInternalDisplay, function() {
-    newDetailedStatus = {MessagesSent: 1};
+    const newDetailedStatus = {MessagesSent: 1};
     webUIListenerCallback('detailed-status-updated', newDetailedStatus);
     assertEquals($('internal-display').value, '{\n  \"MessagesSent\": 1\n}');
   });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 857fdec..a87e43fa 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -111,6 +111,7 @@
     "os_people_page_test.js",
     "os_privacy_page_test.js",
     "os_search_page_test.js",
+    "os_settings_search_box_test.js",
     "parental_controls_page_test.js",
     "people_page_account_manager_test.js",
     "people_page_change_picture_test.js",
@@ -139,6 +140,7 @@
     "timezone_subpage_test.js",
     "tts_subpage_test.js",
     "user_page_tests.js",
+    "wallpaper_subpage_test.js",
   ]
   namespace_rewrites = os_settings_namespace_rewrites +
                        os_test_namespace_rewrites +
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
index 39ab804..8aa8588 100644
--- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -676,6 +676,9 @@
       let pointersPage;
 
       setup(function() {
+        // TODO(crbug.com/1114828): remove this flag setting once the flag has
+        // been removed and this suite merged with the PointingStick suite.
+        loadTimeData.overrideValues({separatePointingStickSettings: false});
         return showAndGetDeviceSubpage('pointers', settings.routes.POINTERS)
             .then(function(page) {
               pointersPage = page;
diff --git a/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js b/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
index ca45fa6..45c0e33 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
@@ -11,7 +11,7 @@
    *
    * @implements {chromeos.settings.mojom.SearchHandlerInterface}
    */
-  class FakeSettingsSearchHandler {
+  /* #export */ class FakeSettingsSearchHandler {
     constructor() {
       /** @private {!Array<chromeos.settings.mojom.SearchResult>} */
       this.fakeResults_ = [];
diff --git a/chrome/test/data/webui/settings/chromeos/fake_user_action_recorder.js b/chrome/test/data/webui/settings/chromeos/fake_user_action_recorder.js
index 9b70b41..a52879b 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_user_action_recorder.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_user_action_recorder.js
@@ -13,7 +13,7 @@
    *
    * @implements {chromeos.settings.mojom.UserActionRecorderInterface}
    */
-  class FakeUserActionRecorder {
+  /* #export */ class FakeUserActionRecorder {
     constructor() {
       this.pageFocusCount = 0;
       this.pageBlurCount = 0;
diff --git a/chrome/test/data/webui/settings/chromeos/os_namespace_rewrites.gni b/chrome/test/data/webui/settings/chromeos/os_namespace_rewrites.gni
index a6e3b36e..9fd7125 100644
--- a/chrome/test/data/webui/settings/chromeos/os_namespace_rewrites.gni
+++ b/chrome/test/data/webui/settings/chromeos/os_namespace_rewrites.gni
@@ -12,6 +12,8 @@
                                "settings.FakeLanguageSettingsPrivate|FakeLanguageSettingsPrivate",
                                "settings.FakeInputMethodPrivate|FakeInputMethodPrivate",
                                "settings.FakeSettingsPrivate|FakeSettingsPrivate",
+                               "settings.FakeSettingsSearchHandler|FakeSettingsSearchHandler",
+                               "settings.FakeUserActionRecorder|FakeUserActionRecorder",
                                "settings.getFakeLanguagePrefs|getFakeLanguagePrefs",
                                "settings.MultiDeviceSettingsMode|MultiDeviceSettingsMode",
                                "settings.TestGuestOsBrowserProxy|TestGuestOsBrowserProxy",
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
index 7eff352..b373587 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
@@ -2,6 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
+// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// #import {Router, Route, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {eventToPromise} from 'chrome://test/test_util.m.js';
+// #import {FakeUserActionRecorder} from './fake_user_action_recorder.m.js';
+// #import {FakeSettingsSearchHandler} from './fake_settings_search_handler.m.js';
+// #import {setSearchHandlerForTesting, setUserActionRecorderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+// clang-format on
+
 /** @fileoverview Runs tests for the OS settings search box. */
 
 suite('OSSettingsSearchBox', () => {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 41bcfd06..42e2fd3b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -231,6 +231,26 @@
       mocha.run();
     });
 
+// eslint-disable-next-line no-var
+var OSSettingsWallpaperSubpageV3Test = class extends OSSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/wallpaper_subpage_test.m.js';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled: super.featureList.enabled.concat(
+          ['chromeos::features::kWallpaperWebUI']),
+    };
+  }
+};
+
+TEST_F('OSSettingsWallpaperSubpageV3Test', 'AllJsTests', () => {
+  mocha.run();
+});
+
 [['AccessibilityPage', 'os_a11y_page_tests.m.js'],
  ['AboutPage', 'os_about_page_tests.m.js'],
  ['AmbientModePage', 'ambient_mode_page_test.m.js'],
@@ -299,6 +319,7 @@
  ['OsEditDictionaryPage', 'os_edit_dictionary_page_test.m.js'],
  ['OsLanguagesPageV2', 'os_languages_page_v2_tests.m.js'],
  ['OsSearchPage', 'os_search_page_test.m.js'],
+ ['OsSettingsSearchBox', 'os_settings_search_box_test.m.js'],
  ['NearbyShareReceiveDialog', 'nearby_share_receive_dialog_tests.m.js'],
  ['ParentalControlsPage', 'parental_controls_page_test.m.js'],
  ['PeoplePage', 'os_people_page_test.m.js'],
diff --git a/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
index fd93225..1a05ec8a 100644
--- a/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
@@ -12,6 +12,7 @@
         'isWallpaperSettingVisible',
         'isWallpaperPolicyControlled',
         'openWallpaperManager',
+        'fetchWallpaperCollections',
       ]);
 
       /** @private */
@@ -19,6 +20,13 @@
 
       /** @private */
       this.isWallpaperPolicyControlled_ = false;
+
+      /**
+       * @private
+       * @type {Array<!WallpaperCollection>}
+       */
+      this.wallpaperCollections_ =
+          [{id: '0', name: 'zero'}, {id: '1', name: 'one'}];
     }
 
     /** @override */
@@ -38,10 +46,23 @@
       this.methodCalled('openWallpaperManager');
     }
 
+    /** @override */
+    fetchWallpaperCollections() {
+      this.methodCalled('fetchWallpaperCollections');
+      return this.wallpaperCollections_.length ?
+          Promise.resolve(this.wallpaperCollections_) :
+          Promise.reject(null);
+    }
+
     /** @param {boolean} Whether the wallpaper is policy controlled. */
     setIsWallpaperPolicyControlled(isPolicyControlled) {
       this.isWallpaperPolicyControlled_ = isPolicyControlled;
     }
+
+    /** @param {Array<!WallpaperCollection>} */
+    setWallpaperCollections(wallpaperCollections) {
+      this.wallpaperCollections_ = wallpaperCollections;
+    }
   }
 
   // #cr_define_end
diff --git a/chrome/test/data/webui/settings/chromeos/wallpaper_subpage_test.js b/chrome/test/data/webui/settings/chromeos/wallpaper_subpage_test.js
new file mode 100644
index 0000000..84f7de7
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/wallpaper_subpage_test.js
@@ -0,0 +1,100 @@
+// Copyright 2021 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.
+
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {WallpaperBrowserProxyImpl, routes, Router} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {TestWallpaperBrowserProxy} from './test_wallpaper_browser_proxy.m.js';
+// #import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+suite('All', function() {
+  /** @type {SettingsWallpaperSubpageElement} */
+  let wallpaperSubpage = null;
+
+  /** @type {?settings.TestWallpaperBrowserProxy} */
+  let browserProxy = null;
+
+  setup(function() {
+    browserProxy = new TestWallpaperBrowserProxy();
+    settings.WallpaperBrowserProxyImpl.instance_ = browserProxy;
+    PolymerTest.clearBody();
+  });
+
+  teardown(function() {
+    if (wallpaperSubpage) {
+      wallpaperSubpage.remove();
+    }
+    settings.Router.getInstance().resetRouteForTesting();
+  });
+
+  function initPage() {
+    wallpaperSubpage = document.createElement('settings-wallpaper-page');
+    document.body.appendChild(wallpaperSubpage);
+    flush();
+  }
+
+  test(
+      'fetches wallpaper collections and shows loading on startup',
+      async () => {
+        initPage();
+        assertEquals(1, browserProxy.getCallCount('fetchWallpaperCollections'));
+
+        const spinner = wallpaperSubpage.$$('paper-spinner-lite');
+        assertTrue(!!spinner);
+        assertTrue(spinner.active);
+        assertFalse(spinner.hidden);
+
+        const ironList = wallpaperSubpage.$$('iron-list');
+        assertFalse(!!ironList);
+      });
+
+  test('shows wallpaper collections when loaded', async () => {
+    initPage();
+
+    const spinner = wallpaperSubpage.$$('paper-spinner-lite');
+    assertTrue(!!spinner);
+    assertTrue(spinner.active);
+
+    await browserProxy.whenCalled('fetchWallpaperCollections');
+    flush();
+
+    assertFalse(spinner.active);
+    assertTrue(spinner.hidden);
+
+    const ironList = wallpaperSubpage.$$('iron-list');
+    assertTrue(!!ironList);
+
+    const elements = ironList.querySelectorAll('.wallpaper-collection-title');
+    assertEquals(2, elements.length);
+
+    assertDeepEquals(wallpaperSubpage.collections_, [
+      {id: '0', name: 'zero'},
+      {id: '1', name: 'one'},
+    ]);
+  });
+
+  test('shows error when fails to load', async () => {
+    browserProxy.setWallpaperCollections([]);
+    initPage();
+
+    const spinner = wallpaperSubpage.$$('paper-spinner-lite');
+    assertTrue(spinner.active);
+
+    // No error displayed while loading.
+    const error = wallpaperSubpage.$$('#error');
+    assertTrue(error.hidden);
+
+    await browserProxy.whenCalled('fetchWallpaperCollections');
+    Polymer.dom.flush();
+
+    assertFalse(spinner.active);
+    assertFalse(error.hidden);
+
+    // No elements should be displayed if there is an error.
+    assertFalse(!!wallpaperSubpage.$$('iron-list'));
+  });
+});
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index b7fa8fb..437613d 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -233,6 +233,9 @@
     // activity hosted by this CastWebContents.
     // No filters implies no restrictions.
     base::Optional<std::vector<std::string>> url_filters = base::nullopt;
+    // Whether WebRTC peer connections are allowed to use legacy versions of the
+    // TLS/DTLS protocols.
+    bool webrtc_allow_legacy_tls_protocols = false;
 
     InitParams();
     InitParams(const InitParams& other);
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 46052f0..046ef39 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -37,6 +37,7 @@
 #include "net/base/net_errors.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
@@ -161,6 +162,11 @@
       renderer_type_ == content::mojom::RendererType::MOJO_RENDERER) {
     renderer_type_ = content::mojom::RendererType::DEFAULT_RENDERER;
   }
+
+  if (init_params.webrtc_allow_legacy_tls_protocols) {
+    web_contents_->GetMutableRendererPrefs()
+        ->webrtc_allow_legacy_tls_protocols = true;
+  }
 }
 
 CastWebContentsImpl::~CastWebContentsImpl() {
diff --git a/chromecast/browser/extensions/cast_extension_system.cc b/chromecast/browser/extensions/cast_extension_system.cc
index d8bf9a7..b0682c4 100644
--- a/chromecast/browser/extensions/cast_extension_system.cc
+++ b/chromecast/browser/extensions/cast_extension_system.cc
@@ -19,7 +19,6 @@
 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_user_script_manager.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/null_app_sorting.h"
 #include "extensions/browser/process_manager.h"
@@ -28,6 +27,7 @@
 #include "extensions/browser/runtime_data.h"
 #include "extensions/browser/service_worker_manager.h"
 #include "extensions/browser/unloaded_extension_reason.h"
+#include "extensions/browser/user_script_manager.h"
 #include "extensions/browser/value_store/value_store_factory_impl.h"
 #include "extensions/common/api/app_runtime.h"
 #include "extensions/common/constants.h"
@@ -186,8 +186,7 @@
 
   RendererStartupHelperFactory::GetForBrowserContext(browser_context_);
 
-  extension_user_script_manager_ =
-      std::make_unique<ExtensionUserScriptManager>(browser_context_);
+  user_script_manager_ = std::make_unique<UserScriptManager>(browser_context_);
 
   extension_registrar_ =
       std::make_unique<ExtensionRegistrar>(browser_context_, this);
@@ -209,9 +208,8 @@
   return service_worker_manager_.get();
 }
 
-ExtensionUserScriptManager*
-CastExtensionSystem::extension_user_script_manager() {
-  return extension_user_script_manager_.get();
+UserScriptManager* CastExtensionSystem::user_script_manager() {
+  return user_script_manager_.get();
 }
 
 StateStore* CastExtensionSystem::state_store() {
diff --git a/chromecast/browser/extensions/cast_extension_system.h b/chromecast/browser/extensions/cast_extension_system.h
index 4261b7f..75a668e 100644
--- a/chromecast/browser/extensions/cast_extension_system.h
+++ b/chromecast/browser/extensions/cast_extension_system.h
@@ -68,7 +68,7 @@
   RuntimeData* runtime_data() override;
   ManagementPolicy* management_policy() override;
   ServiceWorkerManager* service_worker_manager() override;
-  ExtensionUserScriptManager* extension_user_script_manager() override;
+  UserScriptManager* user_script_manager() override;
   StateStore* state_store() override;
   StateStore* rules_store() override;
   scoped_refptr<ValueStoreFactory> store_factory() override;
@@ -124,7 +124,7 @@
   std::unique_ptr<RuntimeData> runtime_data_;
   std::unique_ptr<QuotaService> quota_service_;
   std::unique_ptr<AppSorting> app_sorting_;
-  std::unique_ptr<ExtensionUserScriptManager> extension_user_script_manager_;
+  std::unique_ptr<UserScriptManager> user_script_manager_;
   std::unique_ptr<ExtensionRegistrar> extension_registrar_;
 
   scoped_refptr<ValueStoreFactory> store_factory_;
diff --git a/chromecast/external_mojo/broker_service/broker_service.cc b/chromecast/external_mojo/broker_service/broker_service.cc
index 055f4d7..45606248 100644
--- a/chromecast/external_mojo/broker_service/broker_service.cc
+++ b/chromecast/external_mojo/broker_service/broker_service.cc
@@ -76,8 +76,8 @@
   }
   broker_ = base::SequenceBound<ExternalMojoBroker>(io_thread_->task_runner(),
                                                     GetBrokerPath());
-  broker_.Post(FROM_HERE, &ExternalMojoBroker::InitializeChromium,
-               connector->Clone(), external_services_to_proxy);
+  broker_.AsyncCall(&ExternalMojoBroker::InitializeChromium)
+      .WithArgs(connector->Clone(), external_services_to_proxy);
 }
 
 BrokerService::~BrokerService() {
@@ -134,8 +134,8 @@
 
 void BrokerService::BindConnector(
     mojo::PendingReceiver<mojom::ExternalConnector> receiver) {
-  broker_.Post(FROM_HERE, &ExternalMojoBroker::BindConnector,
-               std::move(receiver));
+  broker_.AsyncCall(&ExternalMojoBroker::BindConnector)
+      .WithArgs(std::move(receiver));
 }
 
 mojo::PendingRemote<mojom::ExternalConnector> BrokerService::CreateConnector() {
diff --git a/chromecast/media/audio/mixer_service/mixer_control.cc b/chromecast/media/audio/mixer_service/mixer_control.cc
index 935135bcf..d8556c6 100644
--- a/chromecast/media/audio/mixer_service/mixer_control.cc
+++ b/chromecast/media/audio/mixer_service/mixer_control.cc
@@ -28,29 +28,29 @@
 
 MixerControl::MixerControl() : control_(AudioIoThread::Get()->task_runner()) {
   DCHECK(HaveFullMixer());
-  control_.Post(FROM_HERE, &ControlConnection::Connect,
-                ControlConnection::ConnectedCallback());
+  control_.AsyncCall(&ControlConnection::Connect)
+      .WithArgs(ControlConnection::ConnectedCallback());
 }
 
 MixerControl::~MixerControl() = default;
 
 void MixerControl::ConfigurePostprocessor(std::string postprocessor_name,
                                           std::string config) {
-  control_.Post(FROM_HERE, &ControlConnection::ConfigurePostprocessor,
-                std::move(postprocessor_name), std::move(config));
+  control_.AsyncCall(&ControlConnection::ConfigurePostprocessor)
+      .WithArgs(std::move(postprocessor_name), std::move(config));
 }
 
 void MixerControl::ListPostprocessors(ListPostprocessorsCallback callback) {
-  control_.Post(FROM_HERE, &ControlConnection::ListPostprocessors,
-                std::move(callback));
+  control_.AsyncCall(&ControlConnection::ListPostprocessors)
+      .WithArgs(std::move(callback));
 }
 void MixerControl::ReloadPostprocessors() {
-  control_.Post(FROM_HERE, &ControlConnection::ReloadPostprocessors);
+  control_.AsyncCall(&ControlConnection::ReloadPostprocessors);
 }
 
 void MixerControl::SetNumOutputChannels(int num_channels) {
-  control_.Post(FROM_HERE, &ControlConnection::SetNumOutputChannels,
-                num_channels);
+  control_.AsyncCall(&ControlConnection::SetNumOutputChannels)
+      .WithArgs(num_channels);
 }
 
 }  // namespace mixer_service
diff --git a/chromecast/media/cma/backend/cast_audio_json.cc b/chromecast/media/cma/backend/cast_audio_json.cc
index b38e0b7b..f79f43f 100644
--- a/chromecast/media/cma/backend/cast_audio_json.cc
+++ b/chromecast/media/cma/backend/cast_audio_json.cc
@@ -87,8 +87,8 @@
 void CastAudioJsonProviderImpl::SetTuningChangedCallback(
     TuningChangedCallback callback) {
   if (cast_audio_watcher_) {
-    cast_audio_watcher_.Post(FROM_HERE, &FileWatcher::SetTuningChangedCallback,
-                             std::move(callback));
+    cast_audio_watcher_.AsyncCall(&FileWatcher::SetTuningChangedCallback)
+        .WithArgs(std::move(callback));
   }
 }
 
diff --git a/chromecast/media/cma/backend/mixer/loopback_handler.cc b/chromecast/media/cma/backend/mixer/loopback_handler.cc
index 9e6c3995..a972097 100644
--- a/chromecast/media/cma/backend/mixer/loopback_handler.cc
+++ b/chromecast/media/cma/backend/mixer/loopback_handler.cc
@@ -160,7 +160,7 @@
 
 void LoopbackHandler::AddConnection(
     std::unique_ptr<MixerLoopbackConnection> connection) {
-  io_.Post(FROM_HERE, &LoopbackIO::AddConnection, std::move(connection));
+  io_.AsyncCall(&LoopbackIO::AddConnection).WithArgs(std::move(connection));
 }
 
 void LoopbackHandler::SetDataSize(int data_size_bytes) {
@@ -169,8 +169,8 @@
   }
 
   if (SetDataSizeInternal(data_size_bytes) && sample_rate_ != 0) {
-    io_.Post(FROM_HERE, &LoopbackIO::SetStreamConfig, format_, sample_rate_,
-             num_channels_, data_size_);
+    io_.AsyncCall(&LoopbackIO::SetStreamConfig)
+        .WithArgs(format_, sample_rate_, num_channels_, data_size_);
   }
 }
 
@@ -220,8 +220,8 @@
     format_ = format;
     sample_rate_ = sample_rate;
     num_channels_ = num_channels;
-    io_.Post(FROM_HERE, &LoopbackIO::SetStreamConfig, format_, sample_rate_,
-             num_channels_, data_size_);
+    io_.AsyncCall(&LoopbackIO::SetStreamConfig)
+        .WithArgs(format_, sample_rate_, num_channels_, data_size_);
   }
 
   DCHECK_LE(data_size_bytes, data_size_);
@@ -229,8 +229,8 @@
   auto buffer = buffer_pool_->GetBuffer();
   memcpy(buffer->data() + mixer_service::MixerSocket::kAudioMessageHeaderSize,
          data, data_size_bytes);
-  io_.Post(FROM_HERE, &LoopbackIO::SendData, std::move(buffer), data_size_bytes,
-           timestamp);
+  io_.AsyncCall(&LoopbackIO::SendData)
+      .WithArgs(std::move(buffer), data_size_bytes, timestamp);
 }
 
 void LoopbackHandler::SendInterruptInternal(LoopbackInterruptReason reason) {
@@ -238,7 +238,7 @@
     return;
   }
 
-  io_.Post(FROM_HERE, &LoopbackIO::SendInterrupt, reason);
+  io_.AsyncCall(&LoopbackIO::SendInterrupt).WithArgs(reason);
 }
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.cc b/chromecast/media/cma/backend/mixer/stream_mixer.cc
index 1630cee..a150358 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer.cc
+++ b/chromecast/media/cma/backend/mixer/stream_mixer.cc
@@ -825,8 +825,8 @@
       sfx != last_sent_sfx_stream_count_) {
     last_sent_primary_stream_count_ = primary;
     last_sent_sfx_stream_count_ = sfx;
-    receiver_.Post(FROM_HERE, &MixerServiceReceiver::OnStreamCountChanged,
-                   primary, sfx);
+    receiver_.AsyncCall(&MixerServiceReceiver::OnStreamCountChanged)
+        .WithArgs(primary, sfx);
   }
 }
 
diff --git a/chromecast/system/reboot/reboot_fuchsia_test.cc b/chromecast/system/reboot/reboot_fuchsia_test.cc
index 8cf0087..d587142 100644
--- a/chromecast/system/reboot/reboot_fuchsia_test.cc
+++ b/chromecast/system/reboot/reboot_fuchsia_test.cc
@@ -183,15 +183,15 @@
 
   StateControlRebootReason GetLastRebootReason() {
     StateControlRebootReason reason;
-    admin_.Post(FROM_HERE, &FakeAdmin::GetLastRebootReason, &reason);
+    admin_.AsyncCall(&FakeAdmin::GetLastRebootReason).WithArgs(&reason);
     thread_.FlushForTesting();
     return reason;
   }
 
   void SetLastReboot(fuchsia::feedback::LastReboot last_reboot) {
-    last_reboot_info_provider_.Post(FROM_HERE,
-                                    &FakeLastRebootInfoProvider::SetLastReboot,
-                                    std::move(last_reboot));
+    last_reboot_info_provider_
+        .AsyncCall(&FakeLastRebootInfoProvider::SetLastReboot)
+        .WithArgs(std::move(last_reboot));
     thread_.FlushForTesting();
   }
 
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index d28da88e..46c5e500 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -610,10 +610,10 @@
         Looking for scanners
       </message>
       <message name="IDS_SCANNING_APP_NO_SCANNERS_TEXT" desc="The text displayed when there are no available scanners found for scanning">
-        No scanner available
+        No scanners available
       </message>
       <message name="IDS_SCANNING_APP_NO_SCANNERS_SUBTEXT" desc="The subtext displayed when there are no available scanners found for scanning">
-        Make sure the scanner is turned on and connected
+        Make sure the scanner is turned on and available via your network or a direct connection
       </message>
       <message name="IDS_SCANNING_APP_LEARN_MORE_BUTTON_LABEL" desc="The label for the button that leads to a help support article to learn more about scanning.">
         Learn more
diff --git a/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_SUBTEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_SUBTEXT.png.sha1
index b2b0e7b5..9db6b35 100644
--- a/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_SUBTEXT.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_SUBTEXT.png.sha1
@@ -1 +1 @@
-1be8957ab5755b57bf03a9fa504e51e75cbee9f6
\ No newline at end of file
+4a11a5e46357fe7cff90015616a1fdaa929a8b50
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_TEXT.png.sha1
index 2d7d0ac..7a0596d 100644
--- a/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_TEXT.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_SCANNING_APP_NO_SCANNERS_TEXT.png.sha1
@@ -1 +1 @@
-699e14de0f66613efa43a0e36501353eee24cfc2
\ No newline at end of file
+4406696638134a3675998d6618b4430cd5b03094
\ No newline at end of file
diff --git a/chromeos/components/diagnostics_ui/BUILD.gn b/chromeos/components/diagnostics_ui/BUILD.gn
index 2d93eb1..39aeabb 100644
--- a/chromeos/components/diagnostics_ui/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/BUILD.gn
@@ -20,7 +20,7 @@
     "//content/public/browser",
     "//ui/base",
     "//ui/resources",
-    "//ui/webui",
+    "//ui/web_dialogs",
   ]
 }
 
diff --git a/chromeos/components/diagnostics_ui/DEPS b/chromeos/components/diagnostics_ui/DEPS
index 3788902..6ea0538 100644
--- a/chromeos/components/diagnostics_ui/DEPS
+++ b/chromeos/components/diagnostics_ui/DEPS
@@ -10,5 +10,5 @@
   "+ui/gfx",
   "+ui/resources",
   "+ui/shell_dialogs",
-  "+ui/webui",
+  "+ui/web_dialogs",
 ]
diff --git a/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc b/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc
index 67271ac..e51e2a8 100644
--- a/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc
+++ b/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc
@@ -674,9 +674,9 @@
   // already disconnected.
   inflight_routine_runner_.reset();
 
-  // Reset `inflight_routine_timer_` so that we do not attempt to fetch the
+  // Stop `inflight_routine_timer_` so that we do not attempt to fetch the
   // status of a cancelled routine.
-  inflight_routine_timer_.reset();
+  inflight_routine_timer_->Stop();
 
   // Make a best effort attempt to remove the routine.
   BindCrosHealthdDiagnosticsServiceIfNeccessary();
@@ -685,6 +685,10 @@
       /*should_include_output=*/false,
       base::BindOnce(&SystemRoutineController::OnRoutineCancelAttempted,
                      base::Unretained(this)));
+
+  // Reset `inflight_routine_id_` to maintain invariant.
+  inflight_routine_id_ = kInvalidRoutineId;
+
   if (IsLoggingEnabled()) {
     routine_log_ptr_->LogRoutineCancelled();
   }
diff --git a/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc b/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc
index 0d21e2b..c0bdf19 100644
--- a/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc
+++ b/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc
@@ -783,5 +783,52 @@
       /*expected_count=*/1);
 }
 
+TEST_F(SystemRoutineControllerTest, CancelThenStartRoutine) {
+  const int32_t expected_id = 1;
+  SetRunRoutineResponse(expected_id,
+                        healthd::DiagnosticRoutineStatusEnum::kRunning);
+
+  auto routine_runner = std::make_unique<FakeRoutineRunner>();
+  system_routine_controller_->RunRoutine(
+      mojom::RoutineType::kCpuStress,
+      routine_runner->receiver.BindNewPipeAndPassRemote());
+  base::RunLoop().RunUntilIdle();
+
+  // Assert that the first routine is not complete.
+  EXPECT_TRUE(routine_runner->result.is_null());
+
+  // Update the status on cros_healthd.
+  SetNonInteractiveRoutineUpdateResponse(
+      /*percent_complete=*/0, healthd::DiagnosticRoutineStatusEnum::kCancelled,
+      mojo::ScopedHandle());
+
+  // Close the routine_runner
+  routine_runner.reset();
+  base::RunLoop().RunUntilIdle();
+
+  SetRunRoutineResponse(/*id=*/1,
+                        healthd::DiagnosticRoutineStatusEnum::kRunning);
+
+  FakeRoutineRunner routine_runner_2;
+  system_routine_controller_->RunRoutine(
+      mojom::RoutineType::kCpuStress,
+      routine_runner_2.receiver.BindNewPipeAndPassRemote());
+  base::RunLoop().RunUntilIdle();
+
+  // Assert that the first routine is not complete.
+  EXPECT_TRUE(routine_runner_2.result.is_null());
+
+  // Update the status on cros_healthd.
+  SetNonInteractiveRoutineUpdateResponse(
+      /*percent_complete=*/100, healthd::DiagnosticRoutineStatusEnum::kPassed,
+      mojo::ScopedHandle());
+
+  // After the update interval, the update is fetched and processed.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(60));
+  EXPECT_FALSE(routine_runner_2.result.is_null());
+  VerifyRoutineResult(*routine_runner_2.result, mojom::RoutineType::kCpuStress,
+                      mojom::StandardRoutineResult::kTestPassed);
+}
+
 }  // namespace diagnostics
 }  // namespace chromeos
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.cc b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
index 3e705e5e..3b70d05f 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.cc
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
@@ -127,11 +127,16 @@
 
 }  // namespace
 
-DiagnosticsUI::DiagnosticsUI(
+DiagnosticsDialogUI::DiagnosticsDialogUI(
     content::WebUI* web_ui,
     const chromeos::diagnostics::SessionLogHandler::SelectFilePolicyCreator&
         select_file_policy_creator)
-    : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true) {
+    : ui::MojoWebDialogUI(web_ui),
+      session_log_handler_(std::make_unique<diagnostics::SessionLogHandler>(
+          select_file_policy_creator)) {
+  diagnostics_manager_ = std::make_unique<diagnostics::DiagnosticsManager>(
+      session_log_handler_.get());
+
   auto html_source = base::WrapUnique(
       content::WebUIDataSource::Create(kChromeUIDiagnosticsAppHost));
   html_source->OverrideContentSecurityPolicy(
@@ -157,12 +162,12 @@
   open_timestamp_ = base::Time::Now();
 }
 
-DiagnosticsUI::~DiagnosticsUI() {
+DiagnosticsDialogUI::~DiagnosticsDialogUI() {
   const base::TimeDelta time_open = base::Time::Now() - open_timestamp_;
   diagnostics::metrics::EmitAppOpenDuration(time_open);
 }
 
-void DiagnosticsUI::BindInterface(
+void DiagnosticsDialogUI::BindInterface(
     mojo::PendingReceiver<diagnostics::mojom::SystemDataProvider> receiver) {
   diagnostics::SystemDataProvider* system_data_provider =
       diagnostics_manager_->GetSystemDataProvider();
@@ -171,7 +176,7 @@
   }
 }
 
-void DiagnosticsUI::BindInterface(
+void DiagnosticsDialogUI::BindInterface(
     mojo::PendingReceiver<diagnostics::mojom::SystemRoutineController>
         receiver) {
   diagnostics::SystemRoutineController* system_routine_controller =
@@ -181,6 +186,6 @@
   }
 }
 
-WEB_UI_CONTROLLER_TYPE_IMPL(DiagnosticsUI)
+WEB_UI_CONTROLLER_TYPE_IMPL(DiagnosticsDialogUI)
 
 }  // namespace chromeos
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.h b/chromeos/components/diagnostics_ui/diagnostics_ui.h
index 002956d..b5a3590 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.h
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.h
@@ -11,11 +11,7 @@
 #include "chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom-forward.h"
 #include "chromeos/components/diagnostics_ui/mojom/system_routine_controller.mojom-forward.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "ui/webui/mojo_web_ui_controller.h"
-
-namespace content {
-class WebUI;
-}  // namespace content
+#include "ui/web_dialogs/web_dialog_ui.h"
 
 namespace chromeos {
 namespace diagnostics {
@@ -24,17 +20,17 @@
 
 }  // namespace diagnostics
 
-// The WebUI for chrome://diagnostics.
-class DiagnosticsUI : public ui::MojoWebUIController {
+// The WebDialogUI for chrome://diagnostics.
+class DiagnosticsDialogUI : public ui::MojoWebDialogUI {
  public:
-  DiagnosticsUI(
+  explicit DiagnosticsDialogUI(
       content::WebUI* web_ui,
       const chromeos::diagnostics::SessionLogHandler::SelectFilePolicyCreator&
           select_file_policy_creator);
-  ~DiagnosticsUI() override;
+  ~DiagnosticsDialogUI() override;
 
-  DiagnosticsUI(const DiagnosticsUI&) = delete;
-  DiagnosticsUI& operator=(const DiagnosticsUI&) = delete;
+  DiagnosticsDialogUI(const DiagnosticsDialogUI&) = delete;
+  DiagnosticsDialogUI& operator=(const DiagnosticsDialogUI&) = delete;
 
   void BindInterface(
       mojo::PendingReceiver<diagnostics::mojom::SystemDataProvider> receiver);
diff --git a/chromeos/components/diagnostics_ui/resources/battery_status_card.html b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
index c40f21e..9a85f29b 100644
--- a/chromeos/components/diagnostics_ui/resources/battery_status_card.html
+++ b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
@@ -5,8 +5,8 @@
 </style>
 
 <diagnostics-card>
-  <div id="cardTitle" slot="title">[[i18n('batteryTitle')]]</div>
-  <div id="batteryStatusChipInfo" slot="chip" class="diagnostics-chip">
+  <div id="cardTitle" slot="title" aria-describedby="batteryStatusChipInfo">[[i18n('batteryTitle')]]</div>
+  <div id="batteryStatusChipInfo" slot="chip" class="diagnostics-chip" aria-hidden="true">
     [[getDesignedFullCharge_(batteryHealth_.chargeFullDesignMilliampHours)]]
   </div>
   <iron-icon slot="icon" icon="[[batteryIcon]]" class$="[[iconClass]]">
diff --git a/chromeos/components/diagnostics_ui/resources/cpu_card.html b/chromeos/components/diagnostics_ui/resources/cpu_card.html
index d22e25b..8bf446c 100644
--- a/chromeos/components/diagnostics_ui/resources/cpu_card.html
+++ b/chromeos/components/diagnostics_ui/resources/cpu_card.html
@@ -1,8 +1,8 @@
 <style include="diagnostics-shared diagnostics-fonts"></style>
 
 <diagnostics-card>
-  <div id="cardTitle" slot="title">[[i18n('cpuTitle')]]</div>
-  <div id="cpuChipInfo" slot="chip">[[cpuChipInfo_]]</div>
+  <div id="cardTitle" slot="title" aria-describedby="cpuChipInfo">[[i18n('cpuTitle')]]</div>
+  <div id="cpuChipInfo" slot="chip" aria-hidden="true">[[cpuChipInfo_]]</div>
   <iron-icon slot="icon" icon="diagnostics:cpu"></iron-icon>
   <!-- TODO(michaelcheco): Add i18n string for percent number format -->
   <realtime-cpu-chart slot="left-panel" id="realtimeCpuChart"
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_card.html b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
index 0c45991..79d19d0 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
@@ -36,13 +36,13 @@
   }
 
   .card-wrapper {
-    border: 1px solid var(--diagnostics-border-color);
-    border-radius: 14px;
+    border-radius: 8px;
+    box-shadow: var(--diagnostics-box-shadow);
     margin: 10px 0;
   }
 
   .data-points {
-    border-bottom: 1px solid var(--diagnostics-border-color);
+    border-bottom: 1px solid var(--google-grey-200);
     display: grid;
     grid-template-columns: 1fr 0fr 1fr 0fr 1fr;
     padding-inline: var(--data-point-container-padding);
@@ -59,7 +59,7 @@
   }
 
   .top-section {
-    border-bottom: 1px solid var(--diagnostics-border-color);
+    border-bottom: 1px solid var(--google-grey-200);
     display: flex;
     margin-top: 16px;
   }
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html b/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
index 18354ee..f657794 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.html
@@ -1,12 +1,12 @@
 <link rel="stylesheet"
     href="chrome://resources/chromeos/colors/cros_colors.generated.css">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
 <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
 <template>
   <style include="cr-shared-style diagnostics-fonts">
     :host {
-      /* TODO(michael) Replace temp hex code when colors are finalized. */
-      --diagnostics-border-color: #c5c7ca;
+      --diagnostics-box-shadow: var(--cr-card-shadow);
     }
 
     html {
diff --git a/chromeos/components/scanning/resources/loading_page.html b/chromeos/components/scanning/resources/loading_page.html
index ce8d1d8..8c099c5 100644
--- a/chromeos/components/scanning/resources/loading_page.html
+++ b/chromeos/components/scanning/resources/loading_page.html
@@ -5,7 +5,6 @@
     flex-direction: column;
     height: calc(100vh - var(--panel-container-margin-top));
     justify-content: center;
-    margin-top: var(--panel-container-margin-top);
     text-align: center;
   }
 
@@ -15,14 +14,18 @@
     font-size: var(--scanning-scanners-loading-font-size);
     font-weight: var(--scanning-medium-font-weight);
     line-height: var(--scanning-scanners-loading-line-height);
+    margin-bottom: 0;
+    margin-top: 32px;
   }
 
   #noScannersSubtext {
     color: var(--scanning-no-scanners-subtext-color);
+    display: inline-block;
     font-family: var(--scanning-no-scanners-subtext-font-family);
     font-size: var(--scanning-no-scanners-subtext-font-size);
     font-weight: var(--scanning-regular-font-weight);
     line-height: var(--scanning-no-scanners-subtext-line-height);
+    margin-top: 16px;
   }
 
   paper-progress {
@@ -30,20 +33,28 @@
     --paper-progress-container-color: var(--google-blue-200);
     border-radius: 4px;
     height: 4px;
-    margin-top: 12px;
-    width: 300px;
+    margin-bottom: 80px;
+    margin-top: 32px;
+    width: 256px;
   }
 
   #buttonDiv {
     margin-top: 32px;
+    margin-bottom: 32px;
+  }
+
+  img {
+    height: 256px;
   }
 </style>
 <div id="loadingContainer">
   <div id="loadingDiv" hidden="[[noScannersAvailable_]]">
+    <img src="scanners_loading.svg" alt="[[i18n('scannersLoadingText')]]">
     <h1>[[i18n('scannersLoadingText')]]</h1>
     <paper-progress indeterminate></paper-progress>
   </div>
   <div id="noScannersDiv" hidden="[[!noScannersAvailable_]]">
+    <img src="no_scanners.svg" alt="[[i18n('noScannersText')]]">
     <h1>[[i18n('noScannersText')]]</h1>
     <span id="noScannersSubtext">[[i18n('noScannersSubtext')]]</span>
     <div id="buttonDiv">
diff --git a/chromeos/components/scanning/resources/no_scanners.svg b/chromeos/components/scanning/resources/no_scanners.svg
new file mode 100644
index 0000000..3fee2246
--- /dev/null
+++ b/chromeos/components/scanning/resources/no_scanners.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
+  <g id="Layer_2" data-name="Layer 2"><g><rect width="256" height="256" fill="#fff"/><path d="M101,174.4c.7.6,5,4.4,10.3,3.4s8.3-6.1,8.6-6.7c1.8-3.2,1.4-5.8,3.6-6.8a4,4,0,0,1,4.2.4c2.2,1.7,1.1,6.1.7,8.1a14.5,14.5,0,0,1-2.8,6,15.2,15.2,0,0,1-6.8,4.7,14.5,14.5,0,0,1-8.5,0" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M96.4,201c6.6,0,13.2-.1,19.7-1.4s12.7-2.9,15.8-8.4c1.6-3,1.8-6.6,1.9-10.1a12.3,12.3,0,0,0-.2-2.7,3.3,3.3,0,0,0-1.8-2" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M114.6,189.4a37.4,37.4,0,0,0,5-.7,15.3,15.3,0,0,0,6.7-2.7,14.3,14.3,0,0,0,2.9-3.6,21,21,0,0,0,2.3-6.5c.4-1.8.5-2.9-.1-4.2A5.9,5.9,0,0,0,129,169" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M101,174.4a22.2,22.2,0,0,1,1.5-3.2l.4-.8.9-1.3c1.2-1.9,2.4-3.9,2.5-6.2a2.3,2.3,0,0,0-.4-1.8,2.3,2.3,0,0,0-2.6-.6,6.6,6.6,0,0,0-2.4,1.5c-1.5,1.8-5.8,7.1-11.5,14.4" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M110.6,133l-5.8-12.7-12.2-.5-3.3,10.4" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="102.6" cy="111.7" r="12.3" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M105.3,114.7a8,8,0,0,0,6.1-1.7" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M109,101.9a9.5,9.5,0,0,0-8.6-2c-2.9.7-5.7,1.1-7.7,3.4s-2.2,4.5-2.8,6.3a9.3,9.3,0,0,0-.5,2.9c0,1.5.5,1.2,1.6.8a53.9,53.9,0,0,0,12.1-6.4A55.7,55.7,0,0,0,109,101.9Z" fill="#4285f4"/><path d="M97.1,102.8c-1.2.5-1.9,3-3.3,3.7.2-.1-2.4-1.8-1.4-3.1a17.5,17.5,0,0,1,3.8-3.4l4.8-3.5,4.6-3.6,2.1-1.3a6.5,6.5,0,0,1,2.7-.8A3.2,3.2,0,0,1,113,92a4.2,4.2,0,0,1-.2,3.6,11.2,11.2,0,0,1,3.5-1.2,2.8,2.8,0,0,1,2.2.4,3.1,3.1,0,0,1,.9,1.9c.3,3-1.6,5.7-3.5,8l-1.3,1.1c-.4.1-2-2.3-2.8-2.8-4-2.4-5.7-4.1-14.7-.2" fill="#4285f4"/><path d="M116.4,193.7a42.3,42.3,0,0,0,5.1-.6,16,16,0,0,0,6.7-2.8,10.6,10.6,0,0,0,2.8-3.5,18.9,18.9,0,0,0,2.4-6.5,7.3,7.3,0,0,0-.1-4.2,5.7,5.7,0,0,0-1.5-2" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M100.3,214.2c-14.6,11.9-34.5,13.5-49.2,4.4-17.9-11-20.3-33.1-17.6-46.5,3.9-19.3,20.9-35.3,43.6-40.5a69,69,0,0,1,12.2-1.7,15,15,0,0,0,10.5,6.9,13.7,13.7,0,0,0,11.3-4.2,45.2,45.2,0,0,1,8.8,2.5c18.9,7.4,27.8,25.3,31,32.6l22.2-9.1,18,28.6c-15.3,17.4-35.7,25.4-52.1,19.9a35.1,35.1,0,0,1-14-9.1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M100.3,214.2c-3.6-12.9-7.3-25.7-11-38.6l-18.9,7.6-.8-16" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><polyline points="98.6 206.7 115.5 206.7 117.9 199.3" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M225.1,37.3C200,14,154.9,17.6,131.8,49.7l-.5,2.7,8.9-1.1a3.5,3.5,0,0,1,3.3,1.4l6.5,8.6a3.4,3.4,0,0,1,.5,3.5l-4.2,9.9a3.7,3.7,0,0,1-2.9,2.2l-10.7,1.3a3.2,3.2,0,0,1-3.2-1.4l-2-2.5-12,68.3,21,17.6L227.4,124C250.4,97.3,247.7,58.2,225.1,37.3Z" fill="#d2e3fc"/><path d="M129.5,52.6a3.7,3.7,0,0,0-2.8,2.2l-4.2,9.9a3.4,3.4,0,0,0,.5,3.5l4.5,6.1,3.8-21.9Z" fill="#fbbc05"/><path d="M104.9,166.9l8.7-10.5,3.5-14.8,21,17.5-14.8,5.6-7.6,9.4c-2.5,4-7.7,4.6-10.4,2.2A7.1,7.1,0,0,1,104.9,166.9Z" fill="#d2e3fc" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="130.5" cy="146.5" r="14.7" fill="#d2e3fc" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M113.6,156.1a7.3,7.3,0,0,0,10.5,8.2" fill="#d2e3fc" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M111.8,74c-6.8-4.7-11.4-4.9-14.5-3.9-6.2,2-6.9,9-13.4,11.6s-10.8.2-15.3-2.1" fill="none" stroke="#e6e7ea" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.63"/><path d="M132.1,134.8a11.9,11.9,0,0,0-12,7.9c-1.9,5.6,1,12.1,6.9,14.7" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M186.7,151.3l-.2.2c2.6-1.2,4.6-3.4,6.9-5.1l2.2-1.5a72.3,72.3,0,0,1,8.7-4.6c1.6-.6,4.1-1.7,5.4-.1a2.8,2.8,0,0,1,0,2.9,8.6,8.6,0,0,1-3.1,2.6l-3.8,2.6c-2.5,1.8-5.1,3.5-7.7,5.1l11.8-7.6c1.2-.7,3.3-2,4.7-2.1s2.9.8,2.3,2.5a5.8,5.8,0,0,1-1.8,2.3,61.6,61.6,0,0,1-6.3,4.1c-3.2,1.9-5.4,3.3-8.1,5.1q3.9-2.7,8.1-5.1l4.1-2.3c1.1-.6,3.1-2.2,4.2-1.5,2.6,1.6-.1,4.2-1.1,4.9L199.9,162l8.2-5.2a12.3,12.3,0,0,1,4.6-2.2,1.9,1.9,0,0,1,2.3,1.1,2.7,2.7,0,0,1-.2,2.6,11,11,0,0,1-3.1,2.6c-3.7,2.5-5.4,3.7-9,6.5s-4.2,5.2-7,7.7a19.1,19.1,0,0,1-8.2,4.3" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M187,163.8a14.6,14.6,0,0,0-.9-12.4c-.3-.4,1.1-2.5,1.4-3s1.5-3.3,1.3-4.9a1.8,1.8,0,0,0-1.6-1.8c-1.1-.2-1.9.7-2.7,1.5a50.3,50.3,0,0,0-10.2,15.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M208.8,106h0a7.3,7.3,0,0,1,10.2,2.4l9.2,14.9a7.5,7.5,0,0,1-2.5,10.3h0a7.4,7.4,0,0,1-10.3-2.5l-9.1-14.9A7.3,7.3,0,0,1,208.8,106Z" fill="none" stroke="#ea4335" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="184.2" cy="73.8" r="30.1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="6"/><path d="M179.8,77.4a8.4,8.4,0,0,1,2.5-3.4,18.3,18.3,0,0,1,4.7-2.4,29.3,29.3,0,0,0,3.8-1.7,5.6,5.6,0,0,0,2-2.5,5.1,5.1,0,0,0,0-4.2,5.7,5.7,0,0,0-3.4-3,5.2,5.2,0,0,0-4.2-.2,8.8,8.8,0,0,0-3.4,2.4l-3-3a11.9,11.9,0,0,1,5.3-3.4,9.8,9.8,0,0,1,6.9.4,12.2,12.2,0,0,1,4.4,3.2,9.2,9.2,0,0,1,1.4,9.4,8.5,8.5,0,0,1-3.1,3.8,23.8,23.8,0,0,1-4.8,2.2,12.6,12.6,0,0,0-3.3,1.7,7.7,7.7,0,0,0-1.9,2.7l-.8,1.9-4-1.7Zm-4.7,11.1a3.4,3.4,0,0,1,1.8-4.2,3.3,3.3,0,0,1,4.1,1.8,2.5,2.5,0,0,1,0,2.4,2.9,2.9,0,0,1-1.7,1.7,2.5,2.5,0,0,1-2.4,0A3.3,3.3,0,0,1,175.1,88.5Z" fill="#4285f4"/><circle cx="231.5" cy="152.7" r="4" fill="#f882ff"/><path d="M83.4,131.1c4.3,8.6,12.9,13.3,20.8,11.9s10.7-6.6,11.7-8" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g></g>
+</svg>
\ No newline at end of file
diff --git a/chromeos/components/scanning/resources/scanners_loading.svg b/chromeos/components/scanning/resources/scanners_loading.svg
new file mode 100644
index 0000000..8cddbed8
--- /dev/null
+++ b/chromeos/components/scanning/resources/scanners_loading.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><g id="Layer_2" data-name="Layer 2">
+<g><rect width="256" height="256" fill="#fff"/><path d="M95.5,186.8H28.8a3.8,3.8,0,0,1-3.9-3.9V41.6a3.8,3.8,0,0,1,3.9-3.9h95.9a3.9,3.9,0,0,1,4,3.9V154.9Z" fill="#d2e3fc"/><path d="M152,160.6l20.4-3.9a141.7,141.7,0,0,0,0,32,135.2,135.2,0,0,0,3.3,18H160.4a169.4,169.4,0,0,1-6-24.1A173.1,173.1,0,0,1,152,160.6Z" fill="#d2e3fc" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M171,156.3l-18.8,3.5c-2.9.1-8.9,2.8-13.4,6.2a62.6,62.6,0,0,0-12.6,12.8" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M165.2,145.6c.7,1.8,1.7,4.7,3,8.3s2.1,4.9,1.8,6.6-3.2,6.1-6.9,5.7a6.5,6.5,0,0,1-5.7-6.2c-.2-2.2-.3-4.4-.5-6.6a8.4,8.4,0,0,0,4.9-1.5A8.9,8.9,0,0,0,165.2,145.6Z" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><ellipse cx="155.2" cy="143.1" rx="10.9" ry="10.8" transform="translate(-6.8 278.4) rotate(-82.5)" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><path d="M144.1,139.7l8.4-4.9,13.4,9c1.5-5.3-2.5-10.8-8.4-12.1S144.8,133.9,144.1,139.7Z" fill="#4285f4"/><path d="M149.1,146.1a4,4,0,0,0,2,2.3,3.2,3.2,0,0,0,3.5-1.1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g id="Sleeve"><path d="M177.9,158.3c1.9-3.7,13.9-2.1,21.7.9,12,4.7,25.5,17.4,27,33.7,1.9,20.4-15.1,41.9-40.9,46.1l-13.5-34.8L194,196a33.7,33.7,0,0,0-1.7-10c-2.5-7-6.7-10.7-12.6-22C177.8,160.4,177.4,159.3,177.9,158.3Z" fill="#fff"/><path d="M169.4,156.6l1.6-.3c27.9-5.3,52.7,13.7,55.3,36.1,2.4,20.8-15,43.4-40.9,46.2l-13.5-34.8,21.8-8.3-2.3-10" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><path id="Had_Fill" data-name="Had Fill" d="M159.1,130.7c-.8-1.9-3.8-2.7-7-2.9a37.2,37.2,0,0,0-17-.2,36.2,36.2,0,0,0-12.1,5.1,40.1,40.1,0,0,0-13.9,14c-4.3,7.3-6.4,16.3-5.4,20.9a29.8,29.8,0,0,1,.7,4.4,24.3,24.3,0,0,1-.4,6l19.2-10c-.1-.5-.5-2-1.1-4s-.9-3.2-.6-4,.8-1.5,3-2.1a36.9,36.9,0,0,0,2.9-2.8,29.7,29.7,0,0,0,6.5-10.8,16.6,16.6,0,0,1,7.8-3.7c4.7-.8,9,1,10.6-1.2a2.4,2.4,0,0,0,.5-1.5,5.3,5.3,0,0,0,3.7-2.4,2.3,2.3,0,0,0,.5-1h.1a3.2,3.2,0,0,0,1.9-1.6A3.1,3.1,0,0,0,159.1,130.7Z" fill="#fff"/><g id="Hand_Srtoke" data-name="Hand Srtoke"><path d="M129.4,134.1a39.2,39.2,0,0,1,13.2-3.1c4.6-.3,13.3,0,14.1,3a3,3,0,0,1-.6,2.2,5.2,5.2,0,0,1-3.6,2.3" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M121.1,160.1a30.9,30.9,0,0,0,5.1-3.6c7.1-6.1,5.6-10.8,10.5-13.8s13.7.4,15.6-3.3a2.7,2.7,0,0,0,.2-2.2c-1.4-3.4-11.6-2.1-13.4-1.8a38.9,38.9,0,0,0-9.8,2.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M129.7,131.2a61.1,61.1,0,0,1,12.8-2.6c3.8-.3,14.7-1.2,16.2,2.7a2.9,2.9,0,0,1-.1,2.3,3,3,0,0,1-1.9,1.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M150.9,127.5a36.7,36.7,0,0,0-29.3,4.8,40.2,40.2,0,0,0-14,15.7,36,36,0,0,0-3.6,11.1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><path d="M127,182.7c-1.9-7.6-3.9-15.1-5.9-22.6l-27.5.5c-10,20.9-2.8,43,11.5,50.2s28.7-1.8,30.3-2.7" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g id="Scope"><path d="M121.1,216.4l-9.4-18.1a8.1,8.1,0,0,1,4-8.5,8.2,8.2,0,0,1,9.2.8c3.7,5.6,7.6,12.1,11.3,17.7" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M140.3,209.2l-8.9-17.8a8.1,8.1,0,0,1,13.3-7.5l11.5,19.4" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M127,195.9a3.5,3.5,0,0,1,1.6-3.9,3.7,3.7,0,0,1,4.7.8l4.2,7.8-6.1,3.3Z" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><ellipse cx="134.6" cy="202.5" rx="3.5" ry="3.3" transform="translate(-29.1 22.7) rotate(-8.7)" fill="#fff" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M128.3,198.3a7.4,7.4,0,0,0-12.1,8.4" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M148.9,191a7.6,7.6,0,0,0-8.6-2.9,7.8,7.8,0,0,0-4.5,9.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="129.3" cy="213.4" r="8.7" fill="#7cb1f2" stroke="#4285f4" stroke-miterlimit="10" stroke-width="2"/><circle cx="148.8" cy="207.8" r="8.7" fill="#7cb1f2" stroke="#4285f4" stroke-miterlimit="10" stroke-width="2"/></g><path id="Hand_Fill" data-name="Hand Fill" d="M149.9,224.8l-4.5,1.1.8,4.1,34.1-2.9c-2.2-5.5-4.3-11-6.5-16.4h-6.7c-9-.2-10.5-.9-14.1-.9-9.9.2-19.3,6.3-21.5,10.2a2.4,2.4,0,0,0-.4,1.9c0,.2.1.3.2.5l1.4-.4a6.3,6.3,0,0,0-1.2,2.6,3.2,3.2,0,0,0,0,2.5,4.7,4.7,0,0,0,.9,1l.4-.2a1.4,1.4,0,0,0-.2.6,3.6,3.6,0,0,0,1.5,3.1,4.5,4.5,0,0,0,4.7-.3,3.7,3.7,0,0,0,1,2.2c2.2,2.2,6.5-1.2,15.1-2.1,6.8-.6,8.3,0,13.8-1a53.5,53.5,0,0,0,11.6-3.2C172.5,224.5,161.8,222.3,149.9,224.8Z" fill="#fff"/><g id="Hand"><path d="M174.2,210.8h-7.1c-8.9-.2-10.5-.9-14-.9-9.9.2-19.3,6.3-21.5,10.2a3,3,0,0,0-.5,1.9l.3.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M180.4,227.2a53.5,53.5,0,0,1-11.6,3.2c-5.5,1-7,.4-13.8,1-8.6.9-12.9,4.3-15.2,2.1a3.8,3.8,0,0,1-.9-2.8c.6-3.4,7.2-4.9,11.1-5.8a42.2,42.2,0,0,1,7.4-1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M154.4,218.9a55.1,55.1,0,0,0-10.2,2.4c-5,1.7-11.2,3.8-11.5,7.2a3.8,3.8,0,0,0,1.4,3.1,4.7,4.7,0,0,0,4.9-.4" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M151.3,214.8a88.7,88.7,0,0,0-9.1,2c-4.3,1.2-6.1,2.1-7.7,3.4a8.8,8.8,0,0,0-2.9,4.4,3.2,3.2,0,0,0,0,2.5,4.7,4.7,0,0,0,.9,1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><line x1="191.4" y1="185.5" x2="190.7" y2="196" fill="#4285f4" stroke="#4285f4" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><path d="M155.6,92.4l-1.2,6.2,3.7-1-.4-1.7A6.6,6.6,0,0,0,155.6,92.4Z" fill="#39b54a"/><path d="M159.3,103.2l-.9-4.3-3.6,1a1.4,1.4,0,0,1-1.3-.3h-.1a1.3,1.3,0,0,1-.3-1.2l1.3-6.7a6.7,6.7,0,0,0-4.2-.5l-7.3,1.6a6.4,6.4,0,0,0-4.8,7.5l1.7,7.3a6.3,6.3,0,0,0,7.4,4.8l7.4-1.6A6.3,6.3,0,0,0,159.3,103.2Z" fill="#34a853"/><path d="M157.5,82.6l12.8,11.6-12.2,3.4.3,1.3,12.3-3.4a1.5,1.5,0,0,0,.6-2.3.1.1,0,0,1-.1-.1L158.4,81.6a1.7,1.7,0,0,0-1.4-.3,1.7,1.7,0,0,0-.9,1.1l-1.7,9.2,1.2.8Z" fill="#4285f4"/><circle cx="241.2" cy="99.8" r="4" fill="#f882ff"/><line x1="24.2" y1="200.5" x2="94.4" y2="200.5" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="157.8" y1="206.7" x2="172" y2="206.7" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="225.5" y1="208" x2="235.9" y2="208" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M171.7,85.3h0a6,6,0,0,1,3.6-7.8l13.3-5.2a6.1,6.1,0,0,1,7.9,3.4h0a6.2,6.2,0,0,1-3.6,7.9l-13.3,5.1A6.1,6.1,0,0,1,171.7,85.3Z" fill="#fbbc05"/><path d="M212.7,88a5.9,5.9,0,0,0,1.9-.5l2.1-.4a6.6,6.6,0,0,1,5.3.8,14.8,14.8,0,0,0,2.7,1.6,5.9,5.9,0,1,0,4.1-10.9,34.2,34.2,0,0,0-4.6-.4,6.8,6.8,0,0,1-4.2-2.1l-1.4-1.6-1.4-1.4a7.9,7.9,0,0,0-3.8-1.8,8.4,8.4,0,0,0-9.6,5.3,8.6,8.6,0,0,0,5.5,11.1A10,10,0,0,0,212.7,88Z" fill="#d2e3fc"/><path d="M162.1,122a8,8,0,0,1,12.2,8.6,5.8,5.8,0,0,1,1.4-1.3,8,8,0,0,1,11,1.9h0a2.9,2.9,0,0,0,4.1.5l.5-.3a8,8,0,0,1,9.9,1.1,6.6,6.6,0,0,1,1.4,2,2.7,2.7,0,0,0,4.1.9l4.7-3.5a5.9,5.9,0,0,1,9.4,3.7h0a5.9,5.9,0,0,1-7.7,6.7L206,140a2.7,2.7,0,0,0-3.3,1.5,8.5,8.5,0,0,1-2.6,3.1,8,8,0,0,1-11-1.9h0a2.8,2.8,0,0,0-4-.6l-.3.2a7.9,7.9,0,0,1-12.2-8.5,8.6,8.6,0,0,1-1.4,1.3,7.9,7.9,0,0,1-11-2A8,8,0,0,1,162.1,122Z" fill="#4285f4"/><path d="M33.4,31.9H25.7a7.6,7.6,0,0,0-7.8,7.3v7.3" fill="none" stroke="aqua" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M120.9,32.4h7.7a7.6,7.6,0,0,1,7.8,7.3v7.2" fill="none" stroke="aqua" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M34.2,192.3H26.4a7.6,7.6,0,0,1-7.7-7.3v-7.3" fill="none" stroke="aqua" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M74.8,55.1A8.5,8.5,0,0,0,71,63.4a11,11,0,0,0,1.6,5.3,6.8,6.8,0,0,0,4.5,3,6.9,6.9,0,0,0,6.2-2.6,11.6,11.6,0,0,0,2.4-6.6,9.2,9.2,0,0,0-1.2-5.7,6.7,6.7,0,0,0-4.4-2.9,8,8,0,0,0-5.3,1.2" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M71.5,59.3a24.6,24.6,0,0,1-4.8,7.6c-1.8,1.7-4.8,3.2-7.4,3" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M89,55.9c-4.5,8.2-4,18-5.2,27.2a27,27,0,0,1-3.1,10.1c-2.3,3.7-7.2,6.8-11.6,5.2A6.7,6.7,0,0,1,65,90.6a14.6,14.6,0,0,1,3.3-5.8c4.8-5.2,11.9-7.2,18.6-9s14.3-3.3,20.5-7.1" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><rect x="18.7" y="83.6" width="115.7" height="4.41" fill="#4285f4"/><line x1="47" y1="110.4" x2="110.5" y2="110.4" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><line x1="47" y1="136.1" x2="110.5" y2="136.1" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><line x1="47" y1="123" x2="110.5" y2="123" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/></g></g>
+</svg>
\ No newline at end of file
diff --git a/chromeos/components/scanning/resources/scanning_app_resources.grd b/chromeos/components/scanning/resources/scanning_app_resources.grd
index aa24fb59..ccae2e2b 100644
--- a/chromeos/components/scanning/resources/scanning_app_resources.grd
+++ b/chromeos/components/scanning/resources/scanning_app_resources.grd
@@ -47,6 +47,8 @@
       <include name="IDR_SCANNING_APP_ICON_128" file="scanning_app_icon_128.png" type="BINDATA" />
       <include name="IDR_SCANNING_APP_ICON_192" file="scanning_app_icon_192.png" type="BINDATA" />
       <include name="IDR_SCANNING_APP_ICON_256" file="scanning_app_icon_256.png" type="BINDATA" />
+      <include name="IDR_SCANNING_APP_SCANNERS_LOADING_SVG" file="scanners_loading.svg" type="BINDATA" />
+      <include name="IDR_SCANNING_APP_NO_SCANNERS_SVG" file="no_scanners.svg" type="BINDATA" />
     </includes>
 
     <structures>
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 92639d4..155ef69 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -57,7 +57,6 @@
     "//components/prefs",
     "//components/signin/public/identity_manager",
     "//components/user_manager",
-    "//media",
     "//services/media_session/public/cpp",
     "//ui/accessibility:ax_assistant",
   ]
@@ -67,8 +66,8 @@
     "//chromeos/services/assistant/public/cpp",
     "//chromeos/services/assistant/public/mojom",
     "//chromeos/services/assistant/public/shared",
-    "//mojo/public/cpp/bindings:bindings",
-    "//services/audio/public/cpp:cpp",
+    "//mojo/public/cpp/bindings",
+    "//services/audio/public/cpp",
   ]
 
   if (enable_cros_libassistant) {
@@ -83,24 +82,10 @@
       "assistant_settings_impl.h",
       "libassistant_service_host_impl.cc",
       "libassistant_service_host_impl.h",
-      "platform/audio_device_owner.cc",
-      "platform/audio_device_owner.h",
-      "platform/audio_input_impl.cc",
-      "platform/audio_input_impl.h",
-      "platform/audio_media_data_source.cc",
-      "platform/audio_media_data_source.h",
-      "platform/audio_output_provider_impl.cc",
-      "platform/audio_output_provider_impl.h",
-      "platform/audio_stream.cc",
-      "platform/audio_stream.h",
-      "platform/audio_stream_handler.cc",
-      "platform/audio_stream_handler.h",
       "platform/file_provider_impl.cc",
       "platform/file_provider_impl.h",
       "platform/network_provider_impl.cc",
       "platform/network_provider_impl.h",
-      "platform/volume_control_impl.cc",
-      "platform/volume_control_impl.h",
       "platform_api_impl.cc",
       "platform_api_impl.h",
       "utils.cc",
@@ -144,7 +129,6 @@
     ":test_support",
     "//ash/constants",
     "//ash/public/cpp/assistant/test_support",
-    "//ash/public/mojom",
     "//base",
     "//base/test:test_support",
     "//chromeos/audio",
@@ -184,7 +168,6 @@
     sources += [
       "assistant_device_settings_delegate_unittest.cc",
       "assistant_manager_service_impl_unittest.cc",
-      "platform/audio_output_provider_impl_unittest.cc",
       "platform/network_provider_impl_unittest.cc",
       "test_support/fake_service_context.cc",
       "test_support/fake_service_context.h",
@@ -200,7 +183,6 @@
       "//chromeos/services/assistant/public/cpp/migration",
       "//chromeos/services/assistant/public/cpp/migration:test_support",
       "//chromeos/services/network_config/public/mojom",
-      "//services/audio/public/cpp:test_support",
     ]
   }
 }
diff --git a/chromeos/services/assistant/DEPS b/chromeos/services/assistant/DEPS
index c2d8a0a8..8fcf8c8 100644
--- a/chromeos/services/assistant/DEPS
+++ b/chromeos/services/assistant/DEPS
@@ -5,7 +5,6 @@
   "+components/signin",
   "+google_apis/gaia",
   "+libassistant",
-  "+media/audio",
   "+media/base",
   "+mojo/public",
   "+services/audio/public",
diff --git a/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc b/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
index dab643f..1bf4d4b 100644
--- a/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
@@ -38,14 +38,10 @@
 
 std::unique_ptr<CrosPlatformApi>
 AssistantManagerServiceDelegateImpl::CreatePlatformApi(
-    mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-        audio_output_delegate,
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-    scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner) {
-  return std::make_unique<PlatformApiImpl>(
-      std::move(audio_output_delegate), platform_delegate,
-      context_->power_manager_client(), context_->main_task_runner(),
-      background_thread_task_runner);
+    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate) {
+  return std::make_unique<PlatformApiImpl>(platform_delegate,
+                                           context_->power_manager_client(),
+                                           context_->main_task_runner());
 }
 
 std::unique_ptr<assistant_client::AssistantManager>
diff --git a/chromeos/services/assistant/assistant_manager_service_delegate_impl.h b/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
index 2684231..6bc7ee5 100644
--- a/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
@@ -26,10 +26,7 @@
       mojo::PendingRemote<chromeos::libassistant::mojom::AudioInputController>
           pending_remote) override;
   std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
-      mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-      scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner)
+      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate)
       override;
   std::unique_ptr<assistant_client::AssistantManager> CreateAssistantManager(
       assistant_client::PlatformApi* platform_api,
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 21a39a7..6d545e8 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -244,10 +244,7 @@
           device_id_override,
           ShouldPutLogsInHomeDirectory())),
       weak_factory_(this) {
-  platform_api_ = delegate_->CreatePlatformApi(
-      audio_output_delegate_->BindNewPipeAndPassRemote(),
-      platform_delegate_.get(),
-      assistant_proxy_->background_thread().task_runner());
+  platform_api_ = delegate_->CreatePlatformApi(platform_delegate_.get());
 
   if (libassistant_service_host) {
     // During unittests a custom host is passed in, so we'll use that one.
@@ -271,8 +268,8 @@
   assistant_proxy_->AddSpeechRecognitionObserver(
       speech_recognition_observer_->BindNewPipeAndPassRemote());
 
+  audio_output_delegate_->Bind(assistant_proxy_->ExtractAudioOutputDelegate());
   platform_delegate_->Bind(assistant_proxy_->ExtractPlatformDelegate());
-
   audio_input_host_ = delegate_->CreateAudioInputHost(
       assistant_proxy_->ExtractAudioInputController());
 
diff --git a/chromeos/services/assistant/platform/audio_input_impl.cc b/chromeos/services/assistant/platform/audio_input_impl.cc
deleted file mode 100644
index ab9a4832..0000000
--- a/chromeos/services/assistant/platform/audio_input_impl.cc
+++ /dev/null
@@ -1,464 +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 "chromeos/services/assistant/platform/audio_input_impl.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/optional.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/timer/timer.h"
-#include "chromeos/services/assistant/platform/audio_stream.h"
-#include "chromeos/services/assistant/public/cpp/assistant_client.h"
-#include "chromeos/services/assistant/public/cpp/features.h"
-#include "chromeos/services/assistant/utils.h"
-#include "libassistant/shared/public/platform_audio_buffer.h"
-#include "media/audio/audio_device_description.h"
-#include "media/base/audio_parameters.h"
-#include "media/base/audio_sample_types.h"
-#include "media/base/channel_layout.h"
-#include "services/audio/public/cpp/device_factory.h"
-#include "services/audio/public/mojom/stream_factory.mojom.h"
-
-namespace chromeos {
-namespace assistant {
-
-namespace {
-
-constexpr assistant_client::BufferFormat kFormatMono{
-    16000 /* sample_rate */, assistant_client::INTERLEAVED_S16, 1 /* channels */
-};
-
-constexpr assistant_client::BufferFormat kFormatStereo{
-    44100 /* sample_rate */, assistant_client::INTERLEAVED_S16, 2 /* channels */
-};
-
-assistant_client::BufferFormat g_current_format = kFormatMono;
-
-class DspHotwordStateManager : public AudioInputImpl::HotwordStateManager {
- public:
-  DspHotwordStateManager(AudioInputImpl* input,
-                         scoped_refptr<base::SequencedTaskRunner> task_runner)
-      : AudioInputImpl::HotwordStateManager(input), task_runner_(task_runner) {
-    DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  }
-
-  // HotwordStateManager overrides:
-  // Runs on main thread.
-  void OnConversationTurnStarted() override {
-    DCHECK(task_runner_->RunsTasksInCurrentSequence());
-    if (second_phase_timer_.IsRunning()) {
-      DCHECK(stream_state_ == StreamState::HOTWORD);
-      second_phase_timer_.Stop();
-    } else {
-      // Handles user click on mic button.
-      input_->RecreateAudioInputStream(false /* use_dsp */);
-    }
-    stream_state_ = StreamState::NORMAL;
-  }
-
-  // Runs on main thread.
-  void OnConversationTurnFinished() override {
-    DCHECK(task_runner_->RunsTasksInCurrentSequence());
-    input_->RecreateAudioInputStream(true /* use_dsp */);
-    if (stream_state_ == StreamState::HOTWORD) {
-      // If |stream_state_| remains unchanged, that indicates the first stage
-      // DSP hotword detection was rejected by Libassistant.
-      RecordDspHotwordDetection(DspHotwordDetectionStatus::SOFTWARE_REJECTED);
-    }
-    stream_state_ = StreamState::HOTWORD;
-  }
-
-  // Runs on audio service thread
-  void OnCaptureDataArrived() override {
-    // Posting to main thread to avoid timer's sequence check error.
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&DspHotwordStateManager::OnCaptureDataArrivedMainThread,
-                       weak_factory_.GetWeakPtr()));
-  }
-
-  void RecreateAudioInputStream() override {
-    DCHECK(task_runner_->RunsTasksInCurrentSequence());
-    input_->RecreateAudioInputStream(stream_state_ == StreamState::HOTWORD);
-  }
-
-  // Runs on main thread.
-  void OnCaptureDataArrivedMainThread() {
-    DCHECK(task_runner_->RunsTasksInCurrentSequence());
-    if (stream_state_ == StreamState::HOTWORD &&
-        !second_phase_timer_.IsRunning()) {
-      RecordDspHotwordDetection(DspHotwordDetectionStatus::HARDWARE_ACCEPTED);
-      // 1s from now, if OnConversationTurnStarted is not called, we assume that
-      // libassistant has rejected the hotword supplied by DSP. Thus, we reset
-      // and reopen the device on hotword state.
-      second_phase_timer_.Start(
-          FROM_HERE, base::TimeDelta::FromSeconds(1),
-          base::BindRepeating(
-              &DspHotwordStateManager::OnConversationTurnFinished,
-              base::Unretained(this)));
-    }
-  }
-
- private:
-  enum class StreamState {
-    HOTWORD,
-    NORMAL,
-  };
-
-  // Defines possible detection states of Dsp hotword. These values are
-  // persisted to logs. Entries should not be renumbered and numeric values
-  // should never be reused. Only append to this enum is allowed if the possible
-  // source grows.
-  enum class DspHotwordDetectionStatus {
-    HARDWARE_ACCEPTED = 0,
-    SOFTWARE_REJECTED = 1,
-    kMaxValue = SOFTWARE_REJECTED
-  };
-
-  // Helper function to record UMA metrics for Dsp hotword detection.
-  void RecordDspHotwordDetection(DspHotwordDetectionStatus status) {
-    base::UmaHistogramEnumeration("Assistant.DspHotwordDetection", status);
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  StreamState stream_state_ = StreamState::HOTWORD;
-  base::OneShotTimer second_phase_timer_;
-  base::WeakPtrFactory<DspHotwordStateManager> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(DspHotwordStateManager);
-};
-
-class AudioInputBufferImpl : public assistant_client::AudioBuffer {
- public:
-  AudioInputBufferImpl(const void* data, uint32_t frame_count)
-      : data_(data), frame_count_(frame_count) {}
-  ~AudioInputBufferImpl() override = default;
-
-  // assistant_client::AudioBuffer overrides:
-  assistant_client::BufferFormat GetFormat() const override {
-    return g_current_format;
-  }
-  const void* GetData() const override { return data_; }
-  void* GetWritableData() override {
-    NOTREACHED();
-    return nullptr;
-  }
-  int GetFrameCount() const override { return frame_count_; }
-
- private:
-  const void* data_;
-  int frame_count_;
-  DISALLOW_COPY_AND_ASSIGN(AudioInputBufferImpl);
-};
-
-}  // namespace
-
-AudioInputImpl::HotwordStateManager::HotwordStateManager(
-    AudioInputImpl* audio_input)
-    : input_(audio_input) {}
-
-void AudioInputImpl::HotwordStateManager::RecreateAudioInputStream() {
-  input_->RecreateAudioInputStream(/*use_dsp=*/false);
-}
-
-AudioInputImpl::AudioInputImpl(
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-    const std::string& device_id)
-    : task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      platform_delegate_(platform_delegate),
-      preferred_device_id_(device_id),
-      weak_factory_(this) {
-  DETACH_FROM_SEQUENCE(observer_sequence_checker_);
-
-  DCHECK(platform_delegate);
-
-  RecreateStateManager();
-  if (features::IsStereoAudioInputEnabled())
-    g_current_format = kFormatStereo;
-  else
-    g_current_format = kFormatMono;
-}
-
-AudioInputImpl::~AudioInputImpl() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  StopRecording();
-}
-
-void AudioInputImpl::RecreateStateManager() {
-  if (IsHotwordAvailable()) {
-    state_manager_ =
-        std::make_unique<DspHotwordStateManager>(this, task_runner_);
-  } else {
-    state_manager_ = std::make_unique<HotwordStateManager>(this);
-  }
-}
-
-// Runs on audio service thread.
-void AudioInputImpl::Capture(const media::AudioBus* audio_source,
-                             base::TimeTicks audio_capture_time,
-                             double volume,
-                             bool key_pressed) {
-  DCHECK_EQ(g_current_format.num_channels, audio_source->channels());
-
-  state_manager_->OnCaptureDataArrived();
-
-  std::vector<int16_t> buffer(audio_source->channels() *
-                              audio_source->frames());
-  audio_source->ToInterleaved<media::SignedInt16SampleTypeTraits>(
-      audio_source->frames(), buffer.data());
-  int64_t time = 0;
-  // Only provide accurate timestamp when eraser is enabled, otherwise it seems
-  // break normal libassistant voice recognition.
-  if (features::IsAudioEraserEnabled())
-    time = audio_capture_time.since_origin().InMicroseconds();
-  AudioInputBufferImpl input_buffer(buffer.data(), audio_source->frames());
-  {
-    base::AutoLock lock(lock_);
-    for (auto* observer : observers_)
-      observer->OnAudioBufferAvailable(input_buffer, time);
-  }
-
-  captured_frames_count_ += audio_source->frames();
-  if (VLOG_IS_ON(1)) {
-    auto now = base::TimeTicks::Now();
-    if ((now - last_frame_count_report_time_) >
-        base::TimeDelta::FromMinutes(2)) {
-      VLOG(1) << "Captured frames: " << captured_frames_count_;
-      last_frame_count_report_time_ = now;
-    }
-  }
-}
-
-// Runs on audio service thread.
-void AudioInputImpl::OnCaptureError(const std::string& message) {
-  LOG(ERROR) << "Capture error " << message;
-  base::AutoLock lock(lock_);
-  for (auto* observer : observers_)
-    observer->OnAudioError(AudioInput::Error::FATAL_ERROR);
-}
-
-// Runs on audio service thread.
-void AudioInputImpl::OnCaptureMuted(bool is_muted) {}
-
-// Run on LibAssistant thread.
-assistant_client::BufferFormat AudioInputImpl::GetFormat() const {
-  return g_current_format;
-}
-
-// Run on LibAssistant thread.
-void AudioInputImpl::AddObserver(
-    assistant_client::AudioInput::Observer* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(observer_sequence_checker_);
-  VLOG(1) << " add observer";
-
-  bool have_first_observer = false;
-  {
-    base::AutoLock lock(lock_);
-    observers_.push_back(observer);
-    have_first_observer = observers_.size() == 1;
-  }
-
-  if (have_first_observer) {
-    // Post to main thread runner to start audio recording. Assistant thread
-    // does not have thread context defined in //base and will fail sequence
-    // check in AudioCapturerSource::Start().
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(&AudioInputImpl::UpdateRecordingState,
-                                          weak_factory_.GetWeakPtr()));
-  }
-}
-
-// Run on LibAssistant thread.
-void AudioInputImpl::RemoveObserver(
-    assistant_client::AudioInput::Observer* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(observer_sequence_checker_);
-  VLOG(1) << "Remove observer";
-
-  bool have_no_observer = false;
-  {
-    base::AutoLock lock(lock_);
-    base::Erase(observers_, observer);
-    have_no_observer = observers_.size() == 0;
-  }
-
-  if (have_no_observer) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(&AudioInputImpl::UpdateRecordingState,
-                                          weak_factory_.GetWeakPtr()));
-
-    // Reset the sequence checker since assistant may call from different thread
-    // after restart.
-    DETACH_FROM_SEQUENCE(observer_sequence_checker_);
-  }
-}
-
-void AudioInputImpl::SetMicState(bool mic_open) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (mic_open_ == mic_open)
-    return;
-
-  mic_open_ = mic_open;
-  UpdateRecordingState();
-}
-
-void AudioInputImpl::OnConversationTurnStarted() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  state_manager_->OnConversationTurnStarted();
-}
-
-void AudioInputImpl::OnConversationTurnFinished() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  state_manager_->OnConversationTurnFinished();
-}
-
-void AudioInputImpl::OnHotwordEnabled(bool enable) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-
-  if (hotword_enabled_ == enable)
-    return;
-
-  hotword_enabled_ = enable;
-  UpdateRecordingState();
-}
-
-void AudioInputImpl::SetDeviceId(const std::string& device_id) {
-  if (preferred_device_id_ == device_id)
-    return;
-
-  preferred_device_id_ = device_id;
-
-  UpdateRecordingState();
-  if (HasOpenAudioStream())
-    state_manager_->RecreateAudioInputStream();
-}
-
-void AudioInputImpl::SetHotwordDeviceId(const std::string& device_id) {
-  if (hotword_device_id_ == device_id)
-    return;
-
-  hotword_device_id_ = device_id;
-  RecreateStateManager();
-  if (HasOpenAudioStream())
-    state_manager_->RecreateAudioInputStream();
-}
-
-void AudioInputImpl::OnLidStateChanged(LidState new_state) {
-  // Lid switch event still gets fired during system suspend, which enables
-  // us to stop DSP recording correctly when user closes lid after the device
-  // goes to sleep.
-  if (new_state != lid_state_) {
-    lid_state_ = new_state;
-    UpdateRecordingState();
-  }
-}
-
-void AudioInputImpl::RecreateAudioInputStream(bool use_dsp) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  StopRecording();
-
-  open_audio_stream_ = std::make_unique<AudioStream>(
-      platform_delegate_, GetDeviceId(use_dsp),
-      ShouldEnableDeadStreamDetection(use_dsp), GetFormat(),
-      /*capture_callback=*/this);
-
-  VLOG(1) << open_audio_stream_->device_id() << " start recording";
-}
-
-bool AudioInputImpl::IsHotwordAvailable() const {
-  return features::IsDspHotwordEnabled() && !hotword_device_id_.empty();
-}
-
-bool AudioInputImpl::IsRecordingForTesting() const {
-  return HasOpenAudioStream();
-}
-
-bool AudioInputImpl::IsUsingHotwordDeviceForTesting() const {
-  return IsRecordingForTesting()  // IN-TEST
-         && GetOpenDeviceId() == hotword_device_id_ && IsHotwordAvailable();
-}
-
-base::Optional<std::string> AudioInputImpl::GetOpenDeviceIdForTesting() const {
-  return GetOpenDeviceId();
-}
-
-base::Optional<bool> AudioInputImpl::IsUsingDeadStreamDetectionForTesting()
-    const {
-  if (!open_audio_stream_)
-    return base::nullopt;
-  return open_audio_stream_->has_dead_stream_detection();
-}
-
-void AudioInputImpl::StartRecording() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(!HasOpenAudioStream());
-  RecreateAudioInputStream(IsHotwordAvailable());
-}
-
-void AudioInputImpl::StopRecording() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (open_audio_stream_) {
-    VLOG(1) << open_audio_stream_->device_id() << " stop recording";
-    VLOG(1) << open_audio_stream_->device_id()
-            << " ending captured frames: " << captured_frames_count_;
-    open_audio_stream_.reset();
-  }
-}
-
-void AudioInputImpl::UpdateRecordingState() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-
-  bool has_observers = false;
-  {
-    base::AutoLock lock(lock_);
-    has_observers = observers_.size() > 0;
-  }
-
-  bool is_lid_closed = (lid_state_ == LidState::kClosed);
-  bool should_enable_hotword =
-      hotword_enabled_ && (!preferred_device_id_.empty());
-  bool should_start =
-      !is_lid_closed && (should_enable_hotword || mic_open_) && has_observers;
-
-  if (!HasOpenAudioStream() && should_start)
-    StartRecording();
-  else if (HasOpenAudioStream() && !should_start)
-    StopRecording();
-}
-
-std::string AudioInputImpl::GetDeviceId(bool use_dsp) const {
-  if (use_dsp && !hotword_device_id_.empty())
-    return hotword_device_id_;
-  else if (!preferred_device_id_.empty())
-    return preferred_device_id_;
-  else
-    return media::AudioDeviceDescription::kDefaultDeviceId;
-}
-
-base::Optional<std::string> AudioInputImpl::GetOpenDeviceId() const {
-  if (!open_audio_stream_)
-    return base::nullopt;
-  return open_audio_stream_->device_id();
-}
-
-bool AudioInputImpl::ShouldEnableDeadStreamDetection(bool use_dsp) const {
-  if (use_dsp && !hotword_device_id_.empty()) {
-    // The DSP device won't provide data until it detects a hotword, so
-    // we disable its dead stream detection.
-    return false;
-  }
-  return true;
-}
-
-bool AudioInputImpl::HasOpenAudioStream() const {
-  return open_audio_stream_ != nullptr;
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_impl.h b/chromeos/services/assistant/platform/audio_input_impl.h
deleted file mode 100644
index e81842dd..0000000
--- a/chromeos/services/assistant/platform/audio_input_impl.h
+++ /dev/null
@@ -1,164 +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 CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_IMPL_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_IMPL_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/observer_list.h"
-#include "base/optional.h"
-#include "base/sequence_checker.h"
-#include "base/synchronization/lock.h"
-#include "base/time/time.h"
-#include "chromeos/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom-forward.h"
-#include "libassistant/shared/public/platform_audio_input.h"
-#include "media/base/audio_capturer_source.h"
-
-namespace chromeos {
-namespace assistant {
-
-class AudioStream;
-
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioInputImpl
-    : public assistant_client::AudioInput,
-      public media::AudioCapturerSource::CaptureCallback {
- public:
-  enum class LidState {
-    kOpen,
-    kClosed,
-  };
-
-  explicit AudioInputImpl(
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-      const std::string& device_id);
-  ~AudioInputImpl() override;
-
-  class HotwordStateManager {
-   public:
-    explicit HotwordStateManager(AudioInputImpl* audio_input_);
-    virtual ~HotwordStateManager() = default;
-    virtual void OnConversationTurnStarted() {}
-    virtual void OnConversationTurnFinished() {}
-    virtual void OnCaptureDataArrived() {}
-    virtual void RecreateAudioInputStream();
-
-   protected:
-    AudioInputImpl* input_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(HotwordStateManager);
-  };
-
-  void RecreateStateManager();
-
-  // media::AudioCapturerSource::CaptureCallback overrides:
-  void Capture(const media::AudioBus* audio_source,
-               base::TimeTicks audio_capture_time,
-               double volume,
-               bool key_pressed) override;
-  void OnCaptureError(const std::string& message) override;
-  void OnCaptureMuted(bool is_muted) override;
-
-  // assistant_client::AudioInput overrides. These function are called by
-  // assistant from assistant thread, for which we should not assume any
-  // //base related thread context to be in place.
-  assistant_client::BufferFormat GetFormat() const override;
-  void AddObserver(assistant_client::AudioInput::Observer* observer) override;
-  void RemoveObserver(
-      assistant_client::AudioInput::Observer* observer) override;
-
-  // Called when the mic state associated with the interaction is changed.
-  void SetMicState(bool mic_open);
-  void OnConversationTurnStarted();
-  void OnConversationTurnFinished();
-
-  // Called when hotword enabled status changed.
-  void OnHotwordEnabled(bool enable);
-
-  void SetDeviceId(const std::string& device_id);
-  void SetHotwordDeviceId(const std::string& device_id);
-
-  // Called when the user opens/closes the lid.
-  void OnLidStateChanged(LidState new_state);
-
-  void RecreateAudioInputStream(bool use_dsp);
-
-  bool IsHotwordAvailable() const;
-
-  // Returns the recording state used in unittests.
-  bool IsRecordingForTesting() const;
-  // Returns if the hotword device is used for recording now.
-  bool IsUsingHotwordDeviceForTesting() const;
-  // Returns the id of the device that is currently recording audio.
-  // Returns nullopt if no audio is being recorded.
-  base::Optional<std::string> GetOpenDeviceIdForTesting() const;
-  // Returns if dead stream detection is being used for the current audio
-  // recording. Returns nullopt if no audio is being recorded.
-  base::Optional<bool> IsUsingDeadStreamDetectionForTesting() const;
-
- private:
-  void StartRecording();
-  void StopRecording();
-  void UpdateRecordingState();
-
-  std::string GetDeviceId(bool use_dsp) const;
-  base::Optional<std::string> GetOpenDeviceId() const;
-  bool ShouldEnableDeadStreamDetection(bool use_dsp) const;
-  bool HasOpenAudioStream() const;
-
-  // User explicitly requested to open microphone.
-  bool mic_open_ = false;
-
-  // Whether hotword is currently enabled.
-  bool hotword_enabled_ = true;
-
-  // Guards observers_;
-  base::Lock lock_;
-  std::vector<assistant_client::AudioInput::Observer*> observers_
-      GUARDED_BY(lock_);
-
-  // This is the total number of frames captured during the life time of this
-  // object. We don't worry about overflow because this count is only used for
-  // logging purposes. If in the future this changes, we should re-evaluate.
-  int captured_frames_count_ = 0;
-  base::TimeTicks last_frame_count_report_time_;
-
-  // To be initialized on assistant thread the first call to AddObserver.
-  // It ensures that AddObserver / RemoveObserver are called on the same
-  // sequence.
-  SEQUENCE_CHECKER(observer_sequence_checker_);
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  std::unique_ptr<HotwordStateManager> state_manager_;
-
-  // Owned by |AssistantManagerServiceImpl|.
-  chromeos::libassistant::mojom::PlatformDelegate* const platform_delegate_;
-
-  // Preferred audio input device which will be used for capture.
-  std::string preferred_device_id_;
-  // Hotword input device used for hardware based hotword detection.
-  std::string hotword_device_id_;
-
-  // Currently open audio stream. nullptr if no audio stream is open.
-  std::unique_ptr<AudioStream> open_audio_stream_;
-
-  // Start with lidstate |kClosed| so we do not open the microphone before we
-  // know if the lid is open or closed.
-  LidState lid_state_ = LidState::kClosed;
-
-  base::WeakPtrFactory<AudioInputImpl> weak_factory_;
-  DISALLOW_COPY_AND_ASSIGN(AudioInputImpl);
-};
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_IMPL_H_
diff --git a/chromeos/services/assistant/platform/audio_output_delegate_impl.cc b/chromeos/services/assistant/platform/audio_output_delegate_impl.cc
index f650424..701c4d64 100644
--- a/chromeos/services/assistant/platform/audio_output_delegate_impl.cc
+++ b/chromeos/services/assistant/platform/audio_output_delegate_impl.cc
@@ -14,6 +14,11 @@
 
 AudioOutputDelegateImpl::~AudioOutputDelegateImpl() = default;
 
+void AudioOutputDelegateImpl::Bind(
+    mojo::PendingReceiver<AudioOutputDelegate> pending_receiver) {
+  receiver_.Bind(std::move(pending_receiver));
+}
+
 void AudioOutputDelegateImpl::RequestAudioFocus(
     libassistant::mojom::AudioOutputStreamType stream_type) {
   // TODO(wutao): Fix the libassistant behavior.
@@ -45,10 +50,5 @@
   media_session_->AddObserver(std::move(observer));
 }
 
-mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-AudioOutputDelegateImpl::BindNewPipeAndPassRemote() {
-  return receiver_.BindNewPipeAndPassRemote();
-}
-
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_output_delegate_impl.h b/chromeos/services/assistant/platform/audio_output_delegate_impl.h
index 55d36ad..8b89e04 100644
--- a/chromeos/services/assistant/platform/audio_output_delegate_impl.h
+++ b/chromeos/services/assistant/platform/audio_output_delegate_impl.h
@@ -23,6 +23,8 @@
   AudioOutputDelegateImpl& operator=(const AudioOutputDelegateImpl&) = delete;
   ~AudioOutputDelegateImpl() override;
 
+  void Bind(mojo::PendingReceiver<AudioOutputDelegate> pending_receiver);
+
   // libassistant::mojom::AudioOutputDelegate implementation:
   void RequestAudioFocus(
       libassistant::mojom::AudioOutputStreamType stream_type) override;
@@ -31,9 +33,6 @@
       mojo::PendingRemote<::media_session::mojom::MediaSessionObserver>
           observer) override;
 
-  mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-  BindNewPipeAndPassRemote();
-
  private:
   mojo::Receiver<AudioOutputDelegate> receiver_{this};
   AssistantMediaSession* const media_session_;
diff --git a/chromeos/services/assistant/platform/audio_stream.cc b/chromeos/services/assistant/platform/audio_stream.cc
deleted file mode 100644
index 4fa1c5de..0000000
--- a/chromeos/services/assistant/platform/audio_stream.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2020 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/assistant/platform/audio_stream.h"
-#include "base/notreached.h"
-#include "chromeos/services/assistant/buildflags.h"
-#include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom.h"
-
-#if BUILDFLAG(ENABLE_FAKE_ASSISTANT_MICROPHONE)
-#include "chromeos/services/assistant/platform/fake_input_device.h"
-#endif  // BUILDFLAG(ENABLE_FAKE_ASSISTANT_MICROPHONE)
-
-namespace chromeos {
-namespace assistant {
-
-namespace {
-
-media::ChannelLayout GetChannelLayout(
-    const assistant_client::BufferFormat& format) {
-  switch (format.num_channels) {
-    case 1:
-      return media::ChannelLayout::CHANNEL_LAYOUT_MONO;
-    case 2:
-      return media::ChannelLayout::CHANNEL_LAYOUT_STEREO;
-    default:
-      NOTREACHED();
-      return media::ChannelLayout::CHANNEL_LAYOUT_UNSUPPORTED;
-  }
-}
-
-}  // namespace
-
-AudioStream::AudioStream(
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-    const std::string& device_id,
-    bool detect_dead_stream,
-    assistant_client::BufferFormat buffer_format,
-    media::AudioCapturerSource::CaptureCallback* capture_callback)
-    : device_id_(device_id),
-      detect_dead_stream_(detect_dead_stream),
-      buffer_format_(buffer_format),
-      platform_delegate_(platform_delegate),
-      capture_callback_(capture_callback) {
-  Start();
-}
-
-AudioStream::~AudioStream() {
-  Stop();
-}
-
-const std::string& AudioStream::device_id() const {
-  return device_id_;
-}
-
-bool AudioStream::has_dead_stream_detection() const {
-  return detect_dead_stream_;
-}
-
-void AudioStream::Start() {
-  mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory;
-
-  platform_delegate_->BindAudioStreamFactory(
-      audio_stream_factory.InitWithNewPipeAndPassReceiver());
-
-#if BUILDFLAG(ENABLE_FAKE_ASSISTANT_MICROPHONE)
-  source_ = CreateFakeInputDevice();
-#else
-  source_ = audio::CreateInputDevice(std::move(audio_stream_factory),
-                                     device_id(), DeadStreamDetection());
-#endif  // BUILDFLAG(ENABLE_FAKE_ASSISTANT_MICROPHONE)
-
-  source_->Initialize(GetAudioParameters(), capture_callback_);
-  source_->Start();
-}
-
-void AudioStream::Stop() {
-  if (source_) {
-    source_->Stop();
-    source_.reset();
-  }
-}
-
-audio::DeadStreamDetection AudioStream::DeadStreamDetection() const {
-  return detect_dead_stream_ ? audio::DeadStreamDetection::kEnabled
-                             : audio::DeadStreamDetection::kDisabled;
-}
-
-media::AudioParameters AudioStream::GetAudioParameters() const {
-  // Provide buffer size for 100 ms
-  int frames_per_buffer = buffer_format_.sample_rate / 10;
-
-  // AUDIO_PCM_LINEAR and AUDIO_PCM_LOW_LATENCY are the same on CRAS.
-  auto result =
-      media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
-                             GetChannelLayout(buffer_format_),
-                             buffer_format_.sample_rate, frames_per_buffer);
-
-  // Set the HOTWORD mask so CRAS knows the device is used for HOTWORD purpose
-  // and is able to conduct the tuning specifically for the scenario. Whether
-  // the HOTWORD is conducted by a hotword device or other devices like
-  // internal mic will be determined by the device_id passed to CRAS.
-  result.set_effects(media::AudioParameters::PlatformEffectsMask::HOTWORD);
-
-  return result;
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_stream.h b/chromeos/services/assistant/platform/audio_stream.h
deleted file mode 100644
index eb81ba9..0000000
--- a/chromeos/services/assistant/platform/audio_stream.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2020 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_ASSISTANT_PLATFORM_AUDIO_STREAM_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_STREAM_H_
-
-#include <string>
-
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom-forward.h"
-#include "libassistant/shared/public/platform_audio_buffer.h"
-#include "media/base/audio_capturer_source.h"
-#include "services/audio/public/cpp/device_factory.h"
-
-namespace chromeos {
-namespace assistant {
-
-// A single audio stream. All captured packets will be sent to the given
-// capture callback.
-// The audio stream will be opened as soon as this class is created, and
-// will be closed in the destructor.
-class AudioStream {
- public:
-  AudioStream(
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-      const std::string& device_id,
-      bool detect_dead_stream,
-      assistant_client::BufferFormat buffer_format,
-      media::AudioCapturerSource::CaptureCallback* capture_callback);
-  AudioStream(const AudioStream&) = delete;
-  AudioStream& operator=(const AudioStream&) = delete;
-  ~AudioStream();
-
-  const std::string& device_id() const;
-
-  bool has_dead_stream_detection() const;
-
- private:
-  void Start();
-
-  void Stop();
-
-  audio::DeadStreamDetection DeadStreamDetection() const;
-
-  media::AudioParameters GetAudioParameters() const;
-
-  // Device used for recording.
-  std::string device_id_;
-  bool detect_dead_stream_;
-  assistant_client::BufferFormat buffer_format_;
-  chromeos::libassistant::mojom::PlatformDelegate* const platform_delegate_;
-  media::AudioCapturerSource::CaptureCallback* const capture_callback_;
-  scoped_refptr<media::AudioCapturerSource> source_;
-  base::WeakPtrFactory<AudioStream> weak_ptr_factory_{this};
-};
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_STREAM_H_
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index 3d9fa9d..43d8d43e 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -8,16 +8,12 @@
 #include <utility>
 #include <vector>
 
-#include "chromeos/services/assistant/platform/audio_devices.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
-#include "chromeos/services/assistant/public/cpp/migration/audio_input_host.h"
 #include "chromeos/services/assistant/utils.h"
 #include "libassistant/shared/public/assistant_export.h"
 #include "libassistant/shared/public/platform_api.h"
 #include "libassistant/shared/public/platform_factory.h"
-#include "media/audio/audio_device_description.h"
 
-using assistant_client::AudioOutputProvider;
 using assistant_client::FileProvider;
 using assistant_client::NetworkProvider;
 using assistant_client::PlatformApi;
@@ -30,24 +26,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 PlatformApiImpl::PlatformApiImpl(
-    mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-        audio_output_delegate,
     chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
     PowerManagerClient* power_manager_client,
-    scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> background_task_runner)
-    : audio_output_provider_(std::move(audio_output_delegate),
-                             platform_delegate,
-                             background_task_runner,
-                             media::AudioDeviceDescription::kDefaultDeviceId),
-      network_provider_(platform_delegate) {}
+    scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner)
+    : network_provider_(platform_delegate) {}
 
 PlatformApiImpl::~PlatformApiImpl() = default;
 
-AudioOutputProvider& PlatformApiImpl::GetAudioOutputProvider() {
-  return audio_output_provider_;
-}
-
 FileProvider& PlatformApiImpl::GetFileProvider() {
   return file_provider_;
 }
diff --git a/chromeos/services/assistant/platform_api_impl.h b/chromeos/services/assistant/platform_api_impl.h
index e18b2a1..dcbc8c7 100644
--- a/chromeos/services/assistant/platform_api_impl.h
+++ b/chromeos/services/assistant/platform_api_impl.h
@@ -9,12 +9,10 @@
 #include <utility>
 #include <vector>
 
-#include "chromeos/services/assistant/platform/audio_output_provider_impl.h"
 #include "chromeos/services/assistant/platform/file_provider_impl.h"
 #include "chromeos/services/assistant/platform/network_provider_impl.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
-#include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom-forward.h"
 #include "libassistant/shared/public/platform_auth.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -28,20 +26,15 @@
 class PlatformApiImpl : public CrosPlatformApi {
  public:
   PlatformApiImpl(
-      mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
       chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
       PowerManagerClient* power_manager_client,
-      scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> background_task_runner);
+      scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner);
   ~PlatformApiImpl() override;
 
-  assistant_client::AudioOutputProvider& GetAudioOutputProvider() override;
   assistant_client::FileProvider& GetFileProvider() override;
   assistant_client::NetworkProvider& GetNetworkProvider() override;
 
  private:
-  AudioOutputProviderImpl audio_output_provider_;
   FileProviderImpl file_provider_;
   NetworkProviderImpl network_provider_;
 
diff --git a/chromeos/services/assistant/proxy/assistant_proxy.cc b/chromeos/services/assistant/proxy/assistant_proxy.cc
index e326265..23731e3 100644
--- a/chromeos/services/assistant/proxy/assistant_proxy.cc
+++ b/chromeos/services/assistant/proxy/assistant_proxy.cc
@@ -80,6 +80,8 @@
         pending_url_loader_factory) {
   mojo::PendingRemote<AudioInputControllerMojom>
       pending_audio_input_controller_remote;
+  mojo::PendingRemote<AudioOutputDelegateMojom>
+      pending_audio_output_delegate_remote;
   mojo::PendingRemote<ConversationControllerMojom>
       pending_conversation_controller_remote;
   mojo::PendingRemote<MediaDelegateMojom> pending_media_delegate_remote;
@@ -90,13 +92,15 @@
       pending_media_delegate_remote.InitWithNewPipeAndPassReceiver();
   mojo::PendingReceiver<PlatformDelegateMojom> pending_platform_delegate =
       pending_platform_delegate_remote.InitWithNewPipeAndPassReceiver();
-
+  pending_audio_output_delegate_receiver_ =
+      pending_audio_output_delegate_remote.InitWithNewPipeAndPassReceiver();
   libassistant_service_remote_->Bind(
       pending_audio_input_controller_remote.InitWithNewPipeAndPassReceiver(),
       pending_conversation_controller_remote.InitWithNewPipeAndPassReceiver(),
       display_controller_remote_.BindNewPipeAndPassReceiver(),
       media_controller_remote_.BindNewPipeAndPassReceiver(),
       pending_service_controller_remote.InitWithNewPipeAndPassReceiver(),
+      std::move(pending_audio_output_delegate_remote),
       std::move(pending_media_delegate_remote),
       std::move(pending_platform_delegate_remote));
 
@@ -128,6 +132,12 @@
   return std::move(audio_input_controller_);
 }
 
+mojo::PendingReceiver<chromeos::libassistant::mojom::AudioOutputDelegate>
+AssistantProxy::ExtractAudioOutputDelegate() {
+  DCHECK(pending_audio_output_delegate_receiver_.is_valid());
+  return std::move(pending_audio_output_delegate_receiver_);
+}
+
 mojo::PendingReceiver<chromeos::libassistant::mojom::MediaDelegate>
 AssistantProxy::ExtractMediaDelegate() {
   DCHECK(media_delegate_.is_valid());
diff --git a/chromeos/services/assistant/proxy/assistant_proxy.h b/chromeos/services/assistant/proxy/assistant_proxy.h
index 0ff7e05..4e78971 100644
--- a/chromeos/services/assistant/proxy/assistant_proxy.h
+++ b/chromeos/services/assistant/proxy/assistant_proxy.h
@@ -8,7 +8,10 @@
 #include <memory>
 
 #include "base/threading/thread.h"
+#include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
+#include "chromeos/services/libassistant/public/mojom/media_controller.mojom.h"
+#include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/service.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -70,6 +73,8 @@
 
   mojo::PendingRemote<chromeos::libassistant::mojom::AudioInputController>
   ExtractAudioInputController();
+  mojo::PendingReceiver<chromeos::libassistant::mojom::AudioOutputDelegate>
+  ExtractAudioOutputDelegate();
   mojo::PendingReceiver<chromeos::libassistant::mojom::MediaDelegate>
   ExtractMediaDelegate();
   mojo::PendingReceiver<chromeos::libassistant::mojom::PlatformDelegate>
@@ -79,6 +84,8 @@
   using AudioInputControllerMojom =
       chromeos::libassistant::mojom::AudioInputController;
   using PlatformDelegateMojom = chromeos::libassistant::mojom::PlatformDelegate;
+  using AudioOutputDelegateMojom =
+      chromeos::libassistant::mojom::AudioOutputDelegate;
   using ConversationControllerMojom =
       chromeos::libassistant::mojom::ConversationController;
   using DisplayControllerMojom =
@@ -116,7 +123,8 @@
   mojo::PendingRemote<AudioInputControllerMojom> audio_input_controller_;
   mojo::PendingReceiver<MediaDelegateMojom> media_delegate_;
   mojo::PendingReceiver<PlatformDelegateMojom> platform_delegate_;
-
+  mojo::PendingReceiver<AudioOutputDelegateMojom>
+      pending_audio_output_delegate_receiver_;
 
   // The thread on which the Libassistant service runs.
   // Warning: must be the last object, so it is destroyed (and flushed) first.
diff --git a/chromeos/services/assistant/proxy/conversation_controller_proxy.cc b/chromeos/services/assistant/proxy/conversation_controller_proxy.cc
index e504b0ab..7eb9147 100644
--- a/chromeos/services/assistant/proxy/conversation_controller_proxy.cc
+++ b/chromeos/services/assistant/proxy/conversation_controller_proxy.cc
@@ -9,35 +9,6 @@
 namespace chromeos {
 namespace assistant {
 
-namespace {
-
-// Converts |AssistantNotification| struct to the corresponding Mojom struct
-// (the Mojom-generated |AssistantNotificationPtr|).
-libassistant::mojom::AssistantNotificationPtr ConvertNotificationToMojomStruct(
-    const AssistantNotification& notification) {
-  auto ptr = libassistant::mojom::AssistantNotification::New();
-  ptr->server_id = notification.server_id;
-  ptr->consistency_token = notification.consistency_token;
-  ptr->opaque_token = notification.opaque_token;
-  ptr->grouping_key = notification.grouping_key;
-  ptr->obfuscated_gaia_id = notification.obfuscated_gaia_id;
-  return ptr;
-}
-
-// Coverts |AssistantFeedback| struct to the corresponding Mojom struct (the
-// Mojom-generated |AssistantFeedbackPtr|).
-libassistant::mojom::AssistantFeedbackPtr ConvertFeedbackToMojomStruct(
-    const AssistantFeedback& feedback) {
-  auto ptr = libassistant::mojom::AssistantFeedback::New();
-  ptr->description = feedback.description;
-  ptr->assistant_debug_info_allowed = feedback.assistant_debug_info_allowed;
-  ptr->screenshot_png = std::vector<uint8_t>(feedback.screenshot_png.begin(),
-                                             feedback.screenshot_png.end());
-  return ptr;
-}
-
-}  // namespace
-
 ConversationControllerProxy::ConversationControllerProxy(
     mojo::PendingRemote<ConversationController> conversation_controller_remote)
     : conversation_controller_remote_(
@@ -61,20 +32,18 @@
 void ConversationControllerProxy::RetrieveNotification(
     const AssistantNotification& notification,
     int32_t action_index) {
-  conversation_controller_remote_->RetrieveNotification(
-      ConvertNotificationToMojomStruct(notification), action_index);
+  conversation_controller_remote_->RetrieveNotification(notification,
+                                                        action_index);
 }
 
 void ConversationControllerProxy::DismissNotification(
     const AssistantNotification& notification) {
-  conversation_controller_remote_->DismissNotification(
-      ConvertNotificationToMojomStruct(notification));
+  conversation_controller_remote_->DismissNotification(notification);
 }
 
 void ConversationControllerProxy::SendAssistantFeedback(
     const AssistantFeedback& feedback) {
-  conversation_controller_remote_->SendAssistantFeedback(
-      ConvertFeedbackToMojomStruct(feedback));
+  conversation_controller_remote_->SendAssistantFeedback(feedback);
 }
 
 }  // namespace assistant
diff --git a/chromeos/services/assistant/public/cpp/assistant_service.cc b/chromeos/services/assistant/public/cpp/assistant_service.cc
index 39d96d4..c97b695 100644
--- a/chromeos/services/assistant/public/cpp/assistant_service.cc
+++ b/chromeos/services/assistant/public/cpp/assistant_service.cc
@@ -13,6 +13,9 @@
 
 }  // namespace
 
+AssistantFeedback::AssistantFeedback() = default;
+AssistantFeedback::~AssistantFeedback() = default;
+
 // static
 AssistantService* AssistantService::Get() {
   return g_instance;
diff --git a/chromeos/services/assistant/public/cpp/assistant_service.h b/chromeos/services/assistant/public/cpp/assistant_service.h
index 2e6dd7e..8b6f8f27 100644
--- a/chromeos/services/assistant/public/cpp/assistant_service.h
+++ b/chromeos/services/assistant/public/cpp/assistant_service.h
@@ -140,6 +140,9 @@
 
 //  Details for Assistant feedback.
 struct COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) AssistantFeedback {
+  AssistantFeedback();
+  ~AssistantFeedback();
+
   // User input to be sent with the feedback report.
   std::string description;
 
@@ -148,7 +151,7 @@
 
   // Screenshot if allowed by user.
   // Raw data (non-encoded binary octets)
-  std::string screenshot_png;
+  std::vector<uint8_t> screenshot_png;
 };
 
 class COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) Assistant {
diff --git a/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h b/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
index 8d17ed01..79011be 100644
--- a/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
+++ b/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
@@ -9,7 +9,6 @@
 
 #include "base/single_thread_task_runner.h"
 #include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom-forward.h"
-#include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom-forward.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 
@@ -41,11 +40,7 @@
           pending_remote) = 0;
 
   virtual std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
-      mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-      scoped_refptr<base::SingleThreadTaskRunner>
-          background_thread_task_runner) = 0;
+      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate) = 0;
 
   virtual std::unique_ptr<assistant_client::AssistantManager>
   CreateAssistantManager(assistant_client::PlatformApi* platform_api,
diff --git a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
index 7d49688..d867175 100644
--- a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
@@ -8,7 +8,6 @@
 #include "base/macros.h"
 
 namespace assistant_client {
-class AudioOutputProvider;
 class FileProvider;
 class NetworkProvider;
 }  // namespace assistant_client
@@ -25,9 +24,6 @@
   CrosPlatformApi() = default;
   virtual ~CrosPlatformApi() = default;
 
-  // Returns the platform's audio output provider.
-  virtual assistant_client::AudioOutputProvider& GetAudioOutputProvider() = 0;
-
   // Returns the file provider to be used by libassistant.
   virtual assistant_client::FileProvider& GetFileProvider() = 0;
 
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
index bcc33a4e5..ae97763 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
+++ b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
@@ -51,10 +51,7 @@
 
 std::unique_ptr<CrosPlatformApi>
 FakeAssistantManagerServiceDelegate::CreatePlatformApi(
-    mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-        audio_output_delegate,
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-    scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner) {
+    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate) {
   return std::make_unique<FakePlatformApi>();
 }
 
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
index 868c879..0c40c45 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
+++ b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
@@ -32,10 +32,7 @@
       mojo::PendingRemote<chromeos::libassistant::mojom::AudioInputController>
           pending_remote) override;
   std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
-      mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
-      scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner)
+      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate)
       override;
   std::unique_ptr<assistant_client::AssistantManager> CreateAssistantManager(
       assistant_client::PlatformApi* platform_api,
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
index 01e05f3f9..a464dd8 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
+++ b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
@@ -6,77 +6,13 @@
 
 #include "base/notreached.h"
 #include "chromeos/services/assistant//public/cpp/migration/fake_platform_api.h"
-#include "libassistant/shared/public/platform_audio_output.h"
 
 namespace chromeos {
 namespace assistant {
 
-namespace {
-
-class FakeVolumeControl : public assistant_client::VolumeControl {
- public:
-  // assistant_client::VolumeControl implementation:
-  void SetAudioFocus(assistant_client::OutputStreamType) override {}
-  float GetSystemVolume() override { return 0.5; }
-  void SetSystemVolume(float new_volume, bool user_initiated) override {}
-  float GetAlarmVolume() override { return 0.5; }
-  void SetAlarmVolume(float new_volume, bool user_initiated) override {}
-  bool IsSystemMuted() override { return false; }
-  void SetSystemMuted(bool muted) override {}
-};
-
-class FakeAudioOutputProvider : public assistant_client::AudioOutputProvider {
- public:
-  FakeAudioOutputProvider() = default;
-  FakeAudioOutputProvider(FakeAudioOutputProvider&) = delete;
-  FakeAudioOutputProvider& operator=(FakeAudioOutputProvider&) = delete;
-  ~FakeAudioOutputProvider() override = default;
-
-  // assistant_client::AudioOutputProvider implementation:
-  assistant_client::AudioOutput* CreateAudioOutput(
-      assistant_client::OutputStreamType type,
-      const assistant_client::OutputStreamFormat& stream_format) override {
-    NOTIMPLEMENTED();
-    abort();
-  }
-
-  std::vector<assistant_client::OutputStreamEncoding>
-  GetSupportedStreamEncodings() override {
-    return {};
-  }
-
-  assistant_client::AudioInput* GetReferenceInput() override {
-    NOTIMPLEMENTED();
-    abort();
-  }
-
-  bool SupportsPlaybackTimestamp() const override { return false; }
-
-  assistant_client::VolumeControl& GetVolumeControl() override {
-    return volume_control_;
-  }
-
-  void RegisterAudioEmittingStateCallback(
-      AudioEmittingStateCallback callback) override {
-    NOTIMPLEMENTED();
-  }
-
- private:
-  FakeVolumeControl volume_control_;
-};
-
-}  // namespace
-
-FakePlatformApi::FakePlatformApi()
-    : audio_output_provider_(std::make_unique<FakeAudioOutputProvider>()) {}
-
+FakePlatformApi::FakePlatformApi() = default;
 FakePlatformApi::~FakePlatformApi() = default;
 
-assistant_client::AudioOutputProvider&
-FakePlatformApi::GetAudioOutputProvider() {
-  return *audio_output_provider_.get();
-}
-
 assistant_client::FileProvider& FakePlatformApi::GetFileProvider() {
   NOTIMPLEMENTED();
   abort();
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
index b22f00c..017dca4 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
@@ -23,12 +23,8 @@
   ~FakePlatformApi() override;
 
   // CrosPlatformApi implementation:
-  assistant_client::AudioOutputProvider& GetAudioOutputProvider() override;
   assistant_client::FileProvider& GetFileProvider() override;
   assistant_client::NetworkProvider& GetNetworkProvider() override;
-
- private:
-  std::unique_ptr<assistant_client::AudioOutputProvider> audio_output_provider_;
 };
 
 }  // namespace assistant
diff --git a/chromeos/services/assistant/test_support/fake_libassistant_service.cc b/chromeos/services/assistant/test_support/fake_libassistant_service.cc
index d1d4dbd..99ab2f5 100644
--- a/chromeos/services/assistant/test_support/fake_libassistant_service.cc
+++ b/chromeos/services/assistant/test_support/fake_libassistant_service.cc
@@ -49,6 +49,8 @@
         media_controller,
     mojo::PendingReceiver<libassistant::mojom::ServiceController>
         service_controller,
+    mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
+        audio_output_delegate,
     mojo::PendingRemote<libassistant::mojom::MediaDelegate> media_delegate,
     mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
         platform_delegate) {
diff --git a/chromeos/services/assistant/test_support/fake_libassistant_service.h b/chromeos/services/assistant/test_support/fake_libassistant_service.h
index 9d518285..b7e1aac 100644
--- a/chromeos/services/assistant/test_support/fake_libassistant_service.h
+++ b/chromeos/services/assistant/test_support/fake_libassistant_service.h
@@ -47,6 +47,8 @@
           media_controller,
       mojo::PendingReceiver<libassistant::mojom::ServiceController>
           service_controller,
+      mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
+          audio_output_delegate,
       mojo::PendingRemote<libassistant::mojom::MediaDelegate> media_delegate,
       mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
           platform_delegate) override;
diff --git a/chromeos/services/cellular_setup/esim_manager.cc b/chromeos/services/cellular_setup/esim_manager.cc
index 5344923..2865096 100644
--- a/chromeos/services/cellular_setup/esim_manager.cc
+++ b/chromeos/services/cellular_setup/esim_manager.cc
@@ -19,13 +19,16 @@
 
 ESimManager::ESimManager()
     : ESimManager(NetworkHandler::Get()->cellular_esim_profile_handler(),
-                  NetworkHandler::Get()->cellular_esim_uninstall_handler()) {}
+                  NetworkHandler::Get()->cellular_esim_uninstall_handler(),
+                  NetworkHandler::Get()->cellular_inhibitor()) {}
 
 ESimManager::ESimManager(
     CellularESimProfileHandler* cellular_esim_profile_handler,
-    CellularESimUninstallHandler* cellular_esim_uninstall_handler)
+    CellularESimUninstallHandler* cellular_esim_uninstall_handler,
+    CellularInhibitor* cellular_inhibitor)
     : cellular_esim_profile_handler_(cellular_esim_profile_handler),
-      cellular_esim_uninstall_handler_(cellular_esim_uninstall_handler) {
+      cellular_esim_uninstall_handler_(cellular_esim_uninstall_handler),
+      cellular_inhibitor_(cellular_inhibitor) {
   HermesManagerClient::Get()->AddObserver(this);
   HermesEuiccClient::Get()->AddObserver(this);
   cellular_esim_profile_handler_->AddObserver(this);
diff --git a/chromeos/services/cellular_setup/esim_manager.h b/chromeos/services/cellular_setup/esim_manager.h
index 875452b1..3c2e7d3 100644
--- a/chromeos/services/cellular_setup/esim_manager.h
+++ b/chromeos/services/cellular_setup/esim_manager.h
@@ -22,6 +22,7 @@
 namespace chromeos {
 
 class CellularESimUninstallHandler;
+class CellularInhibitor;
 
 namespace cellular_setup {
 
@@ -39,7 +40,8 @@
  public:
   ESimManager();
   ESimManager(CellularESimProfileHandler* cellular_esim_profile_handler,
-              CellularESimUninstallHandler* cellular_esim_uninstall_handler);
+              CellularESimUninstallHandler* cellular_esim_uninstall_handler,
+              CellularInhibitor* cellular_inhibitor);
   ESimManager(const ESimManager&) = delete;
   ESimManager& operator=(const ESimManager&) = delete;
   ~ESimManager() override;
@@ -72,6 +74,8 @@
     return cellular_esim_uninstall_handler_;
   }
 
+  CellularInhibitor* cellular_inhibitor() { return cellular_inhibitor_; }
+
  private:
   void UpdateAvailableEuiccs();
   // Removes Euicc objects in |available_euiiccs_| that are not in
@@ -84,6 +88,7 @@
 
   CellularESimProfileHandler* cellular_esim_profile_handler_;
   CellularESimUninstallHandler* cellular_esim_uninstall_handler_;
+  CellularInhibitor* cellular_inhibitor_;
   std::vector<std::unique_ptr<Euicc>> available_euiccs_;
   mojo::RemoteSet<mojom::ESimManagerObserver> observers_;
   mojo::ReceiverSet<mojom::ESimManager> receivers_;
diff --git a/chromeos/services/cellular_setup/esim_profile.cc b/chromeos/services/cellular_setup/esim_profile.cc
index 8421ebf9..7a2ff0be 100644
--- a/chromeos/services/cellular_setup/esim_profile.cc
+++ b/chromeos/services/cellular_setup/esim_profile.cc
@@ -8,6 +8,7 @@
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
 #include "chromeos/network/cellular_esim_profile.h"
 #include "chromeos/network/cellular_esim_uninstall_handler.h"
+#include "chromeos/network/cellular_inhibitor.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/services/cellular_setup/esim_manager.h"
 #include "chromeos/services/cellular_setup/esim_mojo_utils.h"
@@ -48,8 +49,8 @@
 }
 
 ESimProfile::~ESimProfile() {
-  if (uninstall_callback_) {
-    NET_LOG(ERROR) << "Profile destroyed with unfulfilled uninstall callback";
+  if (uninstall_callback_ || set_profile_nickname_callback_) {
+    NET_LOG(ERROR) << "Profile destroyed with unfulfilled callbacks";
   }
 }
 
@@ -122,6 +123,12 @@
 
 void ESimProfile::SetProfileNickname(const base::string16& nickname,
                                      SetProfileNicknameCallback callback) {
+  if (set_profile_nickname_callback_) {
+    NET_LOG(ERROR) << "Set Profile Nickname already in progress.";
+    std::move(callback).Run(mojom::ESimOperationResult::kFailure);
+    return;
+  }
+
   if (properties_->state == mojom::ProfileState::kInstalling ||
       properties_->state == mojom::ProfileState::kPending) {
     NET_LOG(ERROR) << "Set Profile Nickname failed: Profile is not installed.";
@@ -129,12 +136,13 @@
     return;
   }
 
-  HermesProfileClient::Properties* properties =
-      HermesProfileClient::Get()->GetProperties(path_);
-  properties->nick_name().Set(
-      base::UTF16ToUTF8(nickname),
-      base::BindOnce(&ESimProfile::OnProfilePropertySet,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  set_profile_nickname_callback_ = std::move(callback);
+  // Pass has_already_requested_installed_profiles=false so that a
+  // RequestInstalledProfiles call will be made in case this ESimProfile object
+  // was created from cached profile data.
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
+      &ESimProfile::PerformSetProfileNickname, weak_ptr_factory_.GetWeakPtr(),
+      nickname, /*has_already_requested_installed_profiles=*/false));
 }
 
 void ESimProfile::UpdateProperties(
@@ -156,10 +164,19 @@
 }
 
 void ESimProfile::OnProfileRemove() {
+  // Run pending callbacks before profile is removed.
   if (uninstall_callback_) {
-    // Run pending uninstall callback before profile is removed.
+    // This profile could be removed before UninstallHandler returns. Return a
+    // success since the profile will be removed.
     std::move(uninstall_callback_).Run(mojom::ESimOperationResult::kSuccess);
   }
+  if (set_profile_nickname_callback_) {
+    // Setting nickname could trigger a request for installed profiles. If this
+    // profile gets removed at that point, return pending call with failure
+    // result.
+    std::move(set_profile_nickname_callback_)
+        .Run(mojom::ESimOperationResult::kFailure);
+  }
 }
 
 mojo::PendingRemote<mojom::ESimProfile> ESimProfile::CreateRemote() {
@@ -168,6 +185,65 @@
   return esim_profile_remote;
 }
 
+void ESimProfile::PerformSetProfileNickname(
+    const base::string16& nickname,
+    bool has_already_requested_installed_profiles,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
+  if (!inhibit_lock) {
+    NET_LOG(ERROR) << "Error inhibiting cellular device";
+    std::move(set_profile_nickname_callback_)
+        .Run(mojom::ESimOperationResult::kFailure);
+    return;
+  }
+
+  // If a valid profile does not exist on Hermes, then we are using cached
+  // profile data. Make a request for installed profiles so that the profile
+  // will be loaded in hermes.
+  if (!ProfileExistsOnEuicc()) {
+    if (has_already_requested_installed_profiles) {
+      // If the profile doesn't exists and installed profiles have already been
+      // requested then give up and return with error. This profile will get
+      // destroyed when ESimProfileHandler updates.
+      NET_LOG(ERROR) << "Unable to find profile in dbus. path="
+                     << path_.value();
+      std::move(set_profile_nickname_callback_)
+          .Run(mojom::ESimOperationResult::kFailure);
+      return;
+    }
+
+    HermesEuiccClient::Get()->RequestInstalledProfiles(
+        euicc_->path(),
+        base::BindOnce(&ESimProfile::OnRequestInstalledProfilesForSetNickname,
+                       weak_ptr_factory_.GetWeakPtr(), nickname,
+                       std::move(inhibit_lock)));
+    return;
+  }
+
+  HermesProfileClient::Properties* properties =
+      HermesProfileClient::Get()->GetProperties(path_);
+  properties->nick_name().Set(
+      base::UTF16ToUTF8(nickname),
+      base::BindOnce(&ESimProfile::OnProfileNicknameSet,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(inhibit_lock)));
+}
+
+void ESimProfile::OnRequestInstalledProfilesForSetNickname(
+    const base::string16& nickname,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+    HermesResponseStatus status) {
+  if (status != HermesResponseStatus::kSuccess) {
+    NET_LOG(ERROR)
+        << "Error requesting installed profiles for set nickname. status="
+        << static_cast<int>(status);
+    std::move(set_profile_nickname_callback_)
+        .Run(mojom::ESimOperationResult::kFailure);
+    return;
+  }
+  PerformSetProfileNickname(nickname,
+                            /*has_already_requested_intalled_profiles=*/true,
+                            std::move(inhibit_lock));
+}
+
 void ESimProfile::OnPendingProfileInstallResult(
     ProfileInstallResultCallback callback,
     HermesResponseStatus status) {
@@ -198,13 +274,26 @@
   std::move(callback).Run(OperationResultFromStatus(status));
 }
 
-void ESimProfile::OnProfilePropertySet(ESimOperationResultCallback callback,
-                                       bool success) {
+void ESimProfile::OnProfileNicknameSet(
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+    bool success) {
   if (!success) {
     NET_LOG(ERROR) << "ESimProfile property set error.";
   }
-  std::move(callback).Run(success ? mojom::ESimOperationResult::kSuccess
-                                  : mojom::ESimOperationResult::kFailure);
+  std::move(set_profile_nickname_callback_)
+      .Run(success ? mojom::ESimOperationResult::kSuccess
+                   : mojom::ESimOperationResult::kFailure);
+  // inhibit_lock goes out of scope and will uninhibit automatically.
+}
+
+bool ESimProfile::ProfileExistsOnEuicc() {
+  HermesEuiccClient::Properties* euicc_properties =
+      HermesEuiccClient::Get()->GetProperties(euicc_->path());
+  const std::vector<dbus::ObjectPath>& installed_profile_paths =
+      euicc_properties->installed_carrier_profiles().value();
+  auto iter = std::find(installed_profile_paths.begin(),
+                        installed_profile_paths.end(), path_);
+  return iter != installed_profile_paths.end();
 }
 
 }  // namespace cellular_setup
diff --git a/chromeos/services/cellular_setup/esim_profile.h b/chromeos/services/cellular_setup/esim_profile.h
index cf9cbd9..3ad6e18 100644
--- a/chromeos/services/cellular_setup/esim_profile.h
+++ b/chromeos/services/cellular_setup/esim_profile.h
@@ -8,6 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/dbus/hermes/hermes_profile_client.h"
 #include "chromeos/dbus/hermes/hermes_response_status.h"
+#include "chromeos/network/cellular_inhibitor.h"
 #include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
@@ -67,22 +68,30 @@
   using ESimOperationResultCallback =
       base::OnceCallback<void(mojom::ESimOperationResult)>;
 
+  void PerformSetProfileNickname(
+      const base::string16& nickname,
+      bool has_already_requested_installed_profiles,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
+  void OnRequestInstalledProfilesForSetNickname(
+      const base::string16& nickname,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+      HermesResponseStatus status);
   void OnPendingProfileInstallResult(ProfileInstallResultCallback callback,
                                      HermesResponseStatus status);
-  void OnProfileInstallResult(ProfileInstallResultCallback callback,
-                              const std::string& eid,
-                              HermesResponseStatus status,
-                              const dbus::ObjectPath* object_path);
   void OnProfileUninstallResult(bool success);
   void OnESimOperationResult(ESimOperationResultCallback callback,
                              HermesResponseStatus status);
-  void OnProfilePropertySet(ESimOperationResultCallback callback, bool success);
+  void OnProfileNicknameSet(
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+      bool success);
+  bool ProfileExistsOnEuicc();
 
   // Reference to Euicc that owns this profile.
   Euicc* euicc_;
   // Reference to ESimManager that owns Euicc of this profile.
   ESimManager* esim_manager_;
   UninstallProfileCallback uninstall_callback_;
+  SetProfileNicknameCallback set_profile_nickname_callback_;
   mojo::ReceiverSet<mojom::ESimProfile> receiver_set_;
   mojom::ESimProfilePropertiesPtr properties_;
   dbus::ObjectPath path_;
diff --git a/chromeos/services/cellular_setup/esim_test_base.cc b/chromeos/services/cellular_setup/esim_test_base.cc
index 99735b1..3f21ecb 100644
--- a/chromeos/services/cellular_setup/esim_test_base.cc
+++ b/chromeos/services/cellular_setup/esim_test_base.cc
@@ -62,9 +62,9 @@
       std::make_unique<TestCellularESimProfileHandler>();
   cellular_esim_profile_handler_->Init();
 
-  esim_manager_ =
-      std::make_unique<ESimManager>(cellular_esim_profile_handler_.get(),
-                                    cellular_esim_uninstall_handler_.get());
+  esim_manager_ = std::make_unique<ESimManager>(
+      cellular_esim_profile_handler_.get(),
+      cellular_esim_uninstall_handler_.get(), cellular_inhibitor_.get());
   observer_ = std::make_unique<ESimManagerTestObserver>();
   esim_manager_->AddObserver(observer_->GenerateRemote());
 }
diff --git a/chromeos/services/cellular_setup/euicc.cc b/chromeos/services/cellular_setup/euicc.cc
index 8ffb3ad..0375ca5 100644
--- a/chromeos/services/cellular_setup/euicc.cc
+++ b/chromeos/services/cellular_setup/euicc.cc
@@ -10,6 +10,7 @@
 #include "base/optional.h"
 #include "base/strings/strcat.h"
 #include "chromeos/network/cellular_esim_profile.h"
+#include "chromeos/network/cellular_inhibitor.h"
 #include "chromeos/services/cellular_setup/esim_manager.h"
 #include "chromeos/services/cellular_setup/esim_mojo_utils.h"
 #include "chromeos/services/cellular_setup/esim_profile.h"
@@ -77,17 +78,16 @@
   }
 
   // Try installing directly with activation code.
-  HermesEuiccClient::Get()->InstallProfileFromActivationCode(
-      path_, activation_code, confirmation_code,
-      base::BindOnce(&Euicc::OnProfileInstallResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      base::BindOnce(&Euicc::PerformInstallProfileFromActivationCode,
+                     weak_ptr_factory_.GetWeakPtr(), activation_code,
+                     confirmation_code, std::move(callback)));
 }
 
 void Euicc::RequestPendingProfiles(RequestPendingProfilesCallback callback) {
   NET_LOG(EVENT) << "Requesting Pending profiles";
-  HermesEuiccClient::Get()->RequestPendingProfiles(
-      path_, /*root_smds=*/std::string(),
-      base::BindOnce(&Euicc::OnRequestPendingEventsResult,
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      base::BindOnce(&Euicc::PerformRequestPendingProfiles,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
@@ -175,8 +175,28 @@
   return nullptr;
 }
 
+void Euicc::PerformInstallProfileFromActivationCode(
+    const std::string& activation_code,
+    const std::string& confirmation_code,
+    InstallProfileFromActivationCodeCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
+  if (!inhibit_lock) {
+    NET_LOG(ERROR) << "Error inhibiting cellular device";
+    std::move(callback).Run(mojom::ProfileInstallResult::kFailure,
+                            mojo::NullRemote());
+    return;
+  }
+
+  HermesEuiccClient::Get()->InstallProfileFromActivationCode(
+      path_, activation_code, confirmation_code,
+      base::BindOnce(&Euicc::OnProfileInstallResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(inhibit_lock)));
+}
+
 void Euicc::OnProfileInstallResult(
     InstallProfileFromActivationCodeCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
     HermesResponseStatus status,
     const dbus::ObjectPath* object_path) {
   if (status != HermesResponseStatus::kSuccess) {
@@ -198,10 +218,28 @@
   }
   std::move(callback).Run(mojom::ProfileInstallResult::kSuccess,
                           esim_profile->CreateRemote());
+  // inhibit_lock goes out of scope and will uninhibit automatically.
 }
 
-void Euicc::OnRequestPendingEventsResult(
+void Euicc::PerformRequestPendingProfiles(
     RequestPendingProfilesCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
+  if (!inhibit_lock) {
+    NET_LOG(ERROR) << "Error inhibiting cellular device";
+    std::move(callback).Run(mojom::ESimOperationResult::kFailure);
+    return;
+  }
+
+  HermesEuiccClient::Get()->RequestPendingProfiles(
+      path_, /*root_smds=*/std::string(),
+      base::BindOnce(&Euicc::OnRequestPendingProfilesResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(inhibit_lock)));
+}
+
+void Euicc::OnRequestPendingProfilesResult(
+    RequestPendingProfilesCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
     HermesResponseStatus status) {
   if (status != HermesResponseStatus::kSuccess) {
     NET_LOG(ERROR) << "Request Pending events failed status="
@@ -210,6 +248,7 @@
   std::move(callback).Run(status == HermesResponseStatus::kSuccess
                               ? mojom::ESimOperationResult::kSuccess
                               : mojom::ESimOperationResult::kFailure);
+  // inhibit_lock goes out of scope and will uninhibit automatically.
 }
 
 mojom::ProfileInstallResult Euicc::GetPendingProfileInfoFromActivationCode(
diff --git a/chromeos/services/cellular_setup/euicc.h b/chromeos/services/cellular_setup/euicc.h
index bdc9e6d5..c429774b 100644
--- a/chromeos/services/cellular_setup/euicc.h
+++ b/chromeos/services/cellular_setup/euicc.h
@@ -7,6 +7,7 @@
 
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
 #include "chromeos/dbus/hermes/hermes_profile_client.h"
+#include "chromeos/network/cellular_inhibitor.h"
 #include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
@@ -64,11 +65,23 @@
   using ProfileInstallResultCallback =
       base::OnceCallback<void(mojom::ProfileInstallResult)>;
 
-  void OnProfileInstallResult(InstallProfileFromActivationCodeCallback callback,
-                              HermesResponseStatus status,
-                              const dbus::ObjectPath* object_path);
-  void OnRequestPendingEventsResult(RequestPendingProfilesCallback callback,
-                                    HermesResponseStatus status);
+  void PerformInstallProfileFromActivationCode(
+      const std::string& activation_code,
+      const std::string& confirmation_code,
+      InstallProfileFromActivationCodeCallback callback,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
+  void OnProfileInstallResult(
+      InstallProfileFromActivationCodeCallback callback,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+      HermesResponseStatus status,
+      const dbus::ObjectPath* object_path);
+  void PerformRequestPendingProfiles(
+      RequestPendingProfilesCallback callback,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
+  void OnRequestPendingProfilesResult(
+      RequestPendingProfilesCallback callback,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+      HermesResponseStatus status);
   mojom::ProfileInstallResult GetPendingProfileInfoFromActivationCode(
       const std::string& activation_code,
       ESimProfile** profile_info);
diff --git a/chromeos/services/libassistant/BUILD.gn b/chromeos/services/libassistant/BUILD.gn
index 00e1ed2..19df25e 100644
--- a/chromeos/services/libassistant/BUILD.gn
+++ b/chromeos/services/libassistant/BUILD.gn
@@ -34,12 +34,6 @@
 
   sources = [
     "assistant_manager_observer.h",
-    "audio/audio_input_impl.cc",
-    "audio/audio_input_impl.h",
-    "audio/audio_input_provider_impl.cc",
-    "audio/audio_input_provider_impl.h",
-    "audio/audio_input_stream.cc",
-    "audio/audio_input_stream.h",
     "audio_input_controller.cc",
     "audio_input_controller.h",
     "chromium_api_delegate.cc",
@@ -71,8 +65,7 @@
   ]
 
   deps = [
-    ":buildflags",
-    ":fake_input_device",
+    ":audio",
     "//build/util:webkit_version",
     "//chromeos/assistant/internal",
     "//chromeos/assistant/internal:libassistant",
@@ -87,8 +80,6 @@
     "//libassistant/shared/internal_api/c:api_wrappers_entrypoint",
     "//libassistant/shared/public",
     "//libassistant/shared/public:export",
-    "//media",
-    "//services/audio/public/cpp",
     "//services/network/public/cpp",
     "//services/network/public/mojom",
   ]
@@ -96,6 +87,45 @@
   defines = [ "IS_LIBASSISTANT_SERVICE_IMPL" ]
 }
 
+source_set("audio") {
+  visibility = [ ":*" ]
+
+  sources = [
+    "audio/audio_device_owner.cc",
+    "audio/audio_device_owner.h",
+    "audio/audio_input_impl.cc",
+    "audio/audio_input_impl.h",
+    "audio/audio_input_provider_impl.cc",
+    "audio/audio_input_provider_impl.h",
+    "audio/audio_input_stream.cc",
+    "audio/audio_input_stream.h",
+    "audio/audio_media_data_source.cc",
+    "audio/audio_media_data_source.h",
+    "audio/audio_output_provider_impl.cc",
+    "audio/audio_output_provider_impl.h",
+    "audio/audio_stream_handler.cc",
+    "audio/audio_stream_handler.h",
+    "audio/volume_control_impl.cc",
+    "audio/volume_control_impl.h",
+  ]
+
+  deps = [
+    ":buildflags",
+    ":fake_input_device",
+    "//ash/public/mojom",
+    "//base",
+    "//chromeos/services/assistant/public/cpp",
+    "//chromeos/services/libassistant/public/mojom",
+    "//libassistant/shared/internal_api/c:api_wrappers_entrypoint",
+    "//libassistant/shared/public",
+    "//libassistant/shared/public:export",
+    "//media",
+    "//services/audio/public/cpp",
+    "//services/audio/public/mojom",
+    "//services/media_session/public/mojom",
+  ]
+}
+
 source_set("fake_input_device") {
   visibility = [ ":*" ]
 
@@ -130,6 +160,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "audio/audio_output_provider_impl_unittest.cc",
     "audio_input_controller_unittest.cc",
     "media_controller_unittest.cc",
     "power_manager_provider_impl_unittest.cc",
@@ -139,6 +170,7 @@
   ]
 
   deps = [
+    ":audio",
     ":internal",
     ":libassistant",
     ":test_support",
diff --git a/chromeos/services/assistant/platform/audio_device_owner.cc b/chromeos/services/libassistant/audio/audio_device_owner.cc
similarity index 97%
rename from chromeos/services/assistant/platform/audio_device_owner.cc
rename to chromeos/services/libassistant/audio/audio_device_owner.cc
index 16d10eb4..f8e3a606 100644
--- a/chromeos/services/assistant/platform/audio_device_owner.cc
+++ b/chromeos/services/libassistant/audio/audio_device_owner.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/audio_device_owner.h"
+#include "chromeos/services/libassistant/audio/audio_device_owner.h"
 
 #include <algorithm>
 #include <utility>
@@ -13,7 +13,7 @@
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 // A macro which ensures we are running on the background thread.
 #define ENSURE_BACKGROUND_THREAD(method, ...)                               \
@@ -94,7 +94,7 @@
 }
 
 void AudioDeviceOwner::StartOnMainThread(
-    chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
+    mojom::AudioOutputDelegate* audio_output_delegate,
     assistant_client::AudioOutput::Delegate* delegate,
     mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
     const assistant_client::OutputStreamFormat& format) {
@@ -253,5 +253,5 @@
     ScheduleFillLocked(base::TimeTicks::Now());
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_device_owner.h b/chromeos/services/libassistant/audio/audio_device_owner.h
similarity index 89%
rename from chromeos/services/assistant/platform/audio_device_owner.h
rename to chromeos/services/libassistant/audio/audio_device_owner.h
index 010fc3c..bda2fa1 100644
--- a/chromeos/services/assistant/platform/audio_device_owner.h
+++ b/chromeos/services/libassistant/audio/audio_device_owner.h
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICE_OWNER_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICE_OWNER_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_
 
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "base/component_export.h"
 #include "base/macros.h"
 #include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
 #include "libassistant/shared/public/platform_audio_output.h"
@@ -22,11 +21,10 @@
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioDeviceOwner
-    : public media::AudioRendererSink::RenderCallback,
-      media_session::mojom::MediaSessionObserver {
+class AudioDeviceOwner : public media::AudioRendererSink::RenderCallback,
+                         media_session::mojom::MediaSessionObserver {
  public:
   AudioDeviceOwner(
       scoped_refptr<base::SequencedTaskRunner> task_runner,
@@ -107,7 +105,7 @@
   DISALLOW_COPY_AND_ASSIGN(AudioDeviceOwner);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICE_OWNER_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_DEVICE_OWNER_H_
diff --git a/chromeos/services/libassistant/audio/audio_input_impl.h b/chromeos/services/libassistant/audio/audio_input_impl.h
index fca1f0b..57bed55 100644
--- a/chromeos/services/libassistant/audio/audio_input_impl.h
+++ b/chromeos/services/libassistant/audio/audio_input_impl.h
@@ -9,7 +9,6 @@
 #include <string>
 #include <vector>
 
-#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
@@ -29,8 +28,7 @@
 class AudioInputStream;
 class AudioCapturer;
 
-class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) AudioInputImpl
-    : public assistant_client::AudioInput {
+class AudioInputImpl : public assistant_client::AudioInput {
  public:
   explicit AudioInputImpl(const base::Optional<std::string>& device_id);
   AudioInputImpl(const AudioInputImpl&) = delete;
diff --git a/chromeos/services/assistant/platform/audio_media_data_source.cc b/chromeos/services/libassistant/audio/audio_media_data_source.cc
similarity index 88%
rename from chromeos/services/assistant/platform/audio_media_data_source.cc
rename to chromeos/services/libassistant/audio/audio_media_data_source.cc
index d7c804b..0da9a19 100644
--- a/chromeos/services/assistant/platform/audio_media_data_source.cc
+++ b/chromeos/services/libassistant/audio/audio_media_data_source.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/audio_media_data_source.h"
+#include "chromeos/services/libassistant/audio/audio_media_data_source.h"
 
 #include <algorithm>
 
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 namespace {
 
@@ -22,7 +22,7 @@
 }  // namespace
 
 AudioMediaDataSource::AudioMediaDataSource(
-    mojo::PendingReceiver<mojom::AssistantMediaDataSource> receiver,
+    mojo::PendingReceiver<AssistantMediaDataSource> receiver,
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : receiver_(this, std::move(receiver)),
       task_runner_(task_runner),
@@ -37,9 +37,7 @@
   }
 }
 
-void AudioMediaDataSource::Read(
-    uint32_t size,
-    mojom::AssistantMediaDataSource::ReadCallback callback) {
+void AudioMediaDataSource::Read(uint32_t size, ReadCallback callback) {
   // Note: mojom calls are sequenced, so we should not receive a second call to
   // Read() before we consumed the previous |read_callback_|.
   DCHECK(!read_callback_);
@@ -74,5 +72,5 @@
   std::move(read_callback_).Run(source_buffer_);
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_media_data_source.h b/chromeos/services/libassistant/audio/audio_media_data_source.h
similarity index 69%
rename from chromeos/services/assistant/platform/audio_media_data_source.h
rename to chromeos/services/libassistant/audio/audio_media_data_source.h
index f1a9b7254..d9b5f2b 100644
--- a/chromeos/services/assistant/platform/audio_media_data_source.h
+++ b/chromeos/services/libassistant/audio/audio_media_data_source.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_MEDIA_DATA_SOURCE_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_MEDIA_DATA_SOURCE_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_MEDIA_DATA_SOURCE_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_MEDIA_DATA_SOURCE_H_
 
 #include <vector>
 
@@ -16,22 +16,23 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 // Class to provide media data source for audio stream decoder.
 // Internally it will read media data from |delegate_|.
-class AudioMediaDataSource : public mojom::AssistantMediaDataSource {
+class AudioMediaDataSource
+    : public chromeos::assistant::mojom::AssistantMediaDataSource {
  public:
   AudioMediaDataSource(
-      mojo::PendingReceiver<mojom::AssistantMediaDataSource> receiver,
+      mojo::PendingReceiver<
+          chromeos::assistant::mojom::AssistantMediaDataSource> receiver,
       scoped_refptr<base::SequencedTaskRunner> task_runner);
   ~AudioMediaDataSource() override;
 
-  // mojom::MediaDataSource implementation.
+  // chromeos::assistant::mojom::MediaDataSource implementation.
   // Called by utility process. Must be called after |set_delegate()|.
   // The caller must wait for callback to finish before issuing the next read.
-  void Read(uint32_t size,
-            mojom::AssistantMediaDataSource::ReadCallback callback) override;
+  void Read(uint32_t size, ReadCallback callback) override;
 
   // Called by AudioStreamHandler on main thread.
   void set_delegate(assistant_client::AudioOutput::Delegate* delegate) {
@@ -42,7 +43,7 @@
   // Called on main thread.
   void OnFillBuffer(int bytes_filled);
 
-  mojo::Receiver<mojom::AssistantMediaDataSource> receiver_;
+  mojo::Receiver<AssistantMediaDataSource> receiver_;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
@@ -50,14 +51,14 @@
 
   std::vector<uint8_t> source_buffer_;
 
-  mojom::AssistantMediaDataSource::ReadCallback read_callback_;
+  ReadCallback read_callback_;
 
   base::WeakPtrFactory<AudioMediaDataSource> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioMediaDataSource);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_MEDIA_DATA_SOURCE_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_MEDIA_DATA_SOURCE_H_
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl.cc b/chromeos/services/libassistant/audio/audio_output_provider_impl.cc
similarity index 87%
rename from chromeos/services/assistant/platform/audio_output_provider_impl.cc
rename to chromeos/services/libassistant/audio/audio_output_provider_impl.cc
index f6e29b5a..b75e95b 100644
--- a/chromeos/services/assistant/platform/audio_output_provider_impl.cc
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl.cc
@@ -2,21 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/audio_output_provider_impl.h"
+#include "chromeos/services/libassistant/audio/audio_output_provider_impl.h"
 
 #include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/services/assistant/platform/audio_stream_handler.h"
 #include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
+#include "chromeos/services/libassistant/audio/audio_stream_handler.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom.h"
 #include "libassistant/shared/public/platform_audio_buffer.h"
 #include "media/audio/audio_device_description.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 namespace {
 
@@ -33,8 +33,9 @@
       mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
       scoped_refptr<base::SequencedTaskRunner> task_runner,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      mojom::AssistantAudioDecoderFactory* audio_decoder_factory,
-      chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
+      chromeos::assistant::mojom::AssistantAudioDecoderFactory*
+          audio_decoder_factory,
+      mojom::AudioOutputDelegate* audio_output_delegate,
       assistant_client::OutputStreamType type,
       assistant_client::OutputStreamFormat format,
       const std::string& device_id)
@@ -86,7 +87,7 @@
     // acquiring audio focus for the internal media player.
     if (stream_type_ == assistant_client::OutputStreamType::STREAM_MEDIA) {
       audio_output_delegate_->RequestAudioFocus(
-          chromeos::libassistant::mojom::AudioOutputStreamType::kMediaStream);
+          mojom::AudioOutputStreamType::kMediaStream);
     }
 
     if (IsEncodedFormat(format_)) {
@@ -122,9 +123,9 @@
   mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory_;
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
   scoped_refptr<base::SequencedTaskRunner> background_thread_task_runner_;
-  mojom::AssistantAudioDecoderFactory* audio_decoder_factory_;
-  chromeos::libassistant::mojom::AudioOutputDelegate* const
-      audio_output_delegate_;
+  chromeos::assistant::mojom::AssistantAudioDecoderFactory*
+      audio_decoder_factory_;
+  mojom::AudioOutputDelegate* const audio_output_delegate_;
 
   const assistant_client::OutputStreamType stream_type_;
   assistant_client::OutputStreamFormat format_;
@@ -140,21 +141,26 @@
 }  // namespace
 
 AudioOutputProviderImpl::AudioOutputProviderImpl(
-    mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-        audio_output_delegate,
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner,
     const std::string& device_id)
-    : platform_delegate_(platform_delegate),
-      audio_output_delegate_(std::move(audio_output_delegate)),
-      loop_back_input_(platform_delegate_,
-                       media::AudioDeviceDescription::kLoopbackInputDeviceId),
-      volume_control_impl_(audio_output_delegate_.get(), platform_delegate),
+    : loop_back_input_(media::AudioDeviceDescription::kLoopbackInputDeviceId),
+      volume_control_impl_(),
       main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
       background_task_runner_(background_task_runner),
-      device_id_(device_id) {
+      device_id_(device_id) {}
+
+void AudioOutputProviderImpl::Bind(
+    mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
+    mojom::PlatformDelegate* platform_delegate) {
+  platform_delegate_ = platform_delegate;
   platform_delegate_->BindAudioDecoderFactory(
       audio_decoder_factory_.BindNewPipeAndPassReceiver());
+
+  audio_output_delegate_.Bind(std::move(audio_output_delegate));
+
+  volume_control_impl_.Initialize(audio_output_delegate_.get(),
+                                  platform_delegate);
+  loop_back_input_.Initialize(platform_delegate);
 }
 
 AudioOutputProviderImpl::~AudioOutputProviderImpl() = default;
@@ -210,5 +216,5 @@
   platform_delegate_->BindAudioStreamFactory(std::move(receiver));
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl.h b/chromeos/services/libassistant/audio/audio_output_provider_impl.h
similarity index 73%
rename from chromeos/services/assistant/platform/audio_output_provider_impl.h
rename to chromeos/services/libassistant/audio/audio_output_provider_impl.h
index 2a8b21e..649bc7b 100644
--- a/chromeos/services/assistant/platform/audio_output_provider_impl.h
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_PROVIDER_IMPL_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_PROVIDER_IMPL_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_OUTPUT_PROVIDER_IMPL_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_OUTPUT_PROVIDER_IMPL_H_
 
 #include <memory>
 #include <string>
@@ -13,11 +13,11 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "chromeos/services/assistant/platform/audio_device_owner.h"
-#include "chromeos/services/assistant/platform/audio_input_impl.h"
-#include "chromeos/services/assistant/platform/volume_control_impl.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
+#include "chromeos/services/libassistant/audio/audio_device_owner.h"
+#include "chromeos/services/libassistant/audio/audio_input_impl.h"
+#include "chromeos/services/libassistant/audio/volume_control_impl.h"
 #include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom-forward.h"
 #include "libassistant/shared/public/platform_audio_output.h"
@@ -27,18 +27,19 @@
 
 namespace chromeos {
 
-namespace assistant {
+namespace libassistant {
 
 class AudioOutputProviderImpl : public assistant_client::AudioOutputProvider {
  public:
   AudioOutputProviderImpl(
-      mojo::PendingRemote<chromeos::libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
       const std::string& device_id);
   ~AudioOutputProviderImpl() override;
 
+  void Bind(
+      mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
+      mojom::PlatformDelegate* platform_delegate);
+
   // assistant_client::AudioOutputProvider overrides:
   assistant_client::AudioOutput* CreateAudioOutput(
       assistant_client::OutputStreamType type,
@@ -61,23 +62,23 @@
       mojo::PendingReceiver<audio::mojom::StreamFactory> receiver);
 
   // Owned by |AssistantManagerServiceImpl|.
-  chromeos::libassistant::mojom::PlatformDelegate* const platform_delegate_;
+  mojom::PlatformDelegate* platform_delegate_ = nullptr;
 
-  mojo::Remote<chromeos::libassistant::mojom::AudioOutputDelegate>
-      audio_output_delegate_;
+  mojo::Remote<mojom::AudioOutputDelegate> audio_output_delegate_;
 
   AudioInputImpl loop_back_input_;
   VolumeControlImpl volume_control_impl_;
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-  mojo::Remote<mojom::AssistantAudioDecoderFactory> audio_decoder_factory_;
+  mojo::Remote<chromeos::assistant::mojom::AssistantAudioDecoderFactory>
+      audio_decoder_factory_;
   std::string device_id_;
   base::WeakPtrFactory<AudioOutputProviderImpl> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AudioOutputProviderImpl);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_PROVIDER_IMPL_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_OUTPUT_PROVIDER_IMPL_H_
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc b/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
similarity index 96%
rename from chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
rename to chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
index 003ed99..91db2e4 100644
--- a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/audio_output_provider_impl.h"
+#include "chromeos/services/libassistant/audio/audio_output_provider_impl.h"
 
 #include <memory>
 #include <utility>
@@ -13,14 +13,13 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
-#include "chromeos/services/assistant/platform/audio_output_delegate_impl.h"
 #include "libassistant/shared/public/platform_audio_output.h"
 #include "media/base/audio_bus.h"
 #include "media/base/bind_to_current_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 class FakeAudioOutputDelegate : public assistant_client::AudioOutput::Delegate {
  public:
@@ -149,5 +148,5 @@
   EXPECT_TRUE(audio_output_delegate.end_of_stream());
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_stream_handler.cc b/chromeos/services/libassistant/audio/audio_stream_handler.cc
similarity index 92%
rename from chromeos/services/assistant/platform/audio_stream_handler.cc
rename to chromeos/services/libassistant/audio/audio_stream_handler.cc
index 78e72cb..8b14058 100644
--- a/chromeos/services/assistant/platform/audio_stream_handler.cc
+++ b/chromeos/services/libassistant/audio/audio_stream_handler.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/audio_stream_handler.h"
+#include "chromeos/services/libassistant/audio/audio_stream_handler.h"
 
 #include "base/bind.h"
-#include "chromeos/services/assistant/platform/audio_media_data_source.h"
+#include "chromeos/services/libassistant/audio/audio_media_data_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 AudioStreamHandler::AudioStreamHandler(
     scoped_refptr<base::SequencedTaskRunner> task_runner)
@@ -19,13 +19,15 @@
 AudioStreamHandler::~AudioStreamHandler() = default;
 
 void AudioStreamHandler::StartAudioDecoder(
-    mojom::AssistantAudioDecoderFactory* audio_decoder_factory,
+    chromeos::assistant::mojom::AssistantAudioDecoderFactory*
+        audio_decoder_factory,
     assistant_client::AudioOutput::Delegate* delegate,
     InitCB on_inited) {
-  mojo::PendingRemote<mojom::AssistantAudioDecoderClient> client;
+  mojo::PendingRemote<AssistantAudioDecoderClient> client;
   client_receiver_.Bind(client.InitWithNewPipeAndPassReceiver());
 
-  mojo::PendingRemote<mojom::AssistantMediaDataSource> data_source;
+  mojo::PendingRemote<chromeos::assistant::mojom::AssistantMediaDataSource>
+      data_source;
   media_data_source_ = std::make_unique<AudioMediaDataSource>(
       data_source.InitWithNewPipeAndPassReceiver(), task_runner_);
 
@@ -180,5 +182,5 @@
   audio_decoder_->Decode();
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_stream_handler.h b/chromeos/services/libassistant/audio/audio_stream_handler.h
similarity index 82%
rename from chromeos/services/assistant/platform/audio_stream_handler.h
rename to chromeos/services/libassistant/audio/audio_stream_handler.h
index 93c67978..df35db9 100644
--- a/chromeos/services/assistant/platform/audio_stream_handler.h
+++ b/chromeos/services/libassistant/audio/audio_stream_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_STREAM_HANDLER_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_STREAM_HANDLER_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_STREAM_HANDLER_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_STREAM_HANDLER_H_
 
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
@@ -14,12 +14,13 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 class AudioMediaDataSource;
 
-class AudioStreamHandler : public mojom::AssistantAudioDecoderClient,
-                           public assistant_client::AudioOutput::Delegate {
+class AudioStreamHandler
+    : public chromeos::assistant::mojom::AssistantAudioDecoderClient,
+      public assistant_client::AudioOutput::Delegate {
  public:
   using InitCB =
       base::OnceCallback<void(const assistant_client::OutputStreamFormat&)>;
@@ -30,11 +31,12 @@
 
   // Called on main thread.
   void StartAudioDecoder(
-      mojom::AssistantAudioDecoderFactory* audio_decoder_factory,
+      chromeos::assistant::mojom::AssistantAudioDecoderFactory*
+          audio_decoder_factory,
       assistant_client::AudioOutput::Delegate* delegate,
       InitCB start_device_owner_on_main_thread);
 
-  // mojom::AssistantAudioDecoderClient overrides:
+  // chromeos::assistant::mojom::AssistantAudioDecoderClient overrides:
   // Called by |audio_decoder_| on utility thread.
   void OnNewBuffers(const std::vector<std::vector<uint8_t>>& buffers) override;
 
@@ -74,9 +76,10 @@
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   assistant_client::AudioOutput::Delegate* delegate_;
 
-  mojo::Receiver<mojom::AssistantAudioDecoderClient> client_receiver_{this};
+  mojo::Receiver<AssistantAudioDecoderClient> client_receiver_{this};
   std::unique_ptr<AudioMediaDataSource> media_data_source_;
-  mojo::Remote<mojom::AssistantAudioDecoder> audio_decoder_;
+  mojo::Remote<chromeos::assistant::mojom::AssistantAudioDecoder>
+      audio_decoder_;
 
   // True when there is more decoded data.
   bool no_more_data_ = false;
@@ -102,7 +105,7 @@
   DISALLOW_COPY_AND_ASSIGN(AudioStreamHandler);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_STREAM_HANDLER_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_STREAM_HANDLER_H_
diff --git a/chromeos/services/assistant/platform/volume_control_impl.cc b/chromeos/services/libassistant/audio/volume_control_impl.cc
similarity index 88%
rename from chromeos/services/assistant/platform/volume_control_impl.cc
rename to chromeos/services/libassistant/audio/volume_control_impl.cc
index cb8ca7a0..3825b26 100644
--- a/chromeos/services/assistant/platform/volume_control_impl.cc
+++ b/chromeos/services/libassistant/audio/volume_control_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/volume_control_impl.h"
+#include "chromeos/services/libassistant/audio/volume_control_impl.h"
 
 #include <utility>
 
@@ -34,15 +34,18 @@
 };
 
 }  // namespace mojo
-namespace chromeos {
-namespace assistant {
 
-VolumeControlImpl::VolumeControlImpl(
-    chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
-    chromeos::libassistant::mojom::PlatformDelegate* platform_delegate)
-    : audio_output_delegate_(audio_output_delegate),
-      main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      weak_factory_(this) {
+namespace chromeos {
+namespace libassistant {
+
+VolumeControlImpl::VolumeControlImpl()
+    : main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {}
+
+void VolumeControlImpl::Initialize(
+    mojom::AudioOutputDelegate* audio_output_delegate,
+    mojom::PlatformDelegate* platform_delegate) {
+  audio_output_delegate_ = audio_output_delegate;
   platform_delegate->BindAssistantVolumeControl(
       volume_control_.BindNewPipeAndPassReceiver());
   mojo::PendingRemote<ash::mojom::VolumeObserver> observer;
@@ -115,5 +118,5 @@
   volume_control_->SetMuted(muted);
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/volume_control_impl.h b/chromeos/services/libassistant/audio/volume_control_impl.h
similarity index 79%
rename from chromeos/services/assistant/platform/volume_control_impl.h
rename to chromeos/services/libassistant/audio/volume_control_impl.h
index f076486..574afa2 100644
--- a/chromeos/services/assistant/platform/volume_control_impl.h
+++ b/chromeos/services/libassistant/audio/volume_control_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_VOLUME_CONTROL_IMPL_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_VOLUME_CONTROL_IMPL_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_VOLUME_CONTROL_IMPL_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_VOLUME_CONTROL_IMPL_H_
 
 #include "ash/public/mojom/assistant_volume_control.mojom.h"
 #include "base/macros.h"
@@ -14,16 +14,17 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 class VolumeControlImpl : public assistant_client::VolumeControl,
                           public ash::mojom::VolumeObserver {
  public:
-  VolumeControlImpl(
-      chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
-      chromeos::libassistant::mojom::PlatformDelegate* platform_delegate);
+  VolumeControlImpl();
   ~VolumeControlImpl() override;
 
+  void Initialize(mojom::AudioOutputDelegate* audio_output_delegate,
+                  mojom::PlatformDelegate* platform_delegate);
+
   // assistant_client::VolumeControl overrides:
   void SetAudioFocus(
       assistant_client::OutputStreamType focused_stream) override;
@@ -45,8 +46,7 @@
   void SetSystemMutedOnMainThread(bool muted);
 
   // Owned by |AudioOutputProviderImpl|.
-  chromeos::libassistant::mojom::AudioOutputDelegate* const
-      audio_output_delegate_;
+  mojom::AudioOutputDelegate* audio_output_delegate_ = nullptr;
   mojo::Remote<ash::mojom::AssistantVolumeControl> volume_control_;
   mojo::Receiver<ash::mojom::VolumeObserver> receiver_{this};
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
@@ -59,7 +59,7 @@
   DISALLOW_COPY_AND_ASSIGN(VolumeControlImpl);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_VOLUME_CONTROL_IMPL_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_VOLUME_CONTROL_IMPL_H_
diff --git a/chromeos/services/libassistant/conversation_controller.cc b/chromeos/services/libassistant/conversation_controller.cc
index b9e96ba2..2f10e42 100644
--- a/chromeos/services/libassistant/conversation_controller.cc
+++ b/chromeos/services/libassistant/conversation_controller.cc
@@ -66,12 +66,12 @@
 }
 
 void ConversationController::RetrieveNotification(
-    mojom::AssistantNotificationPtr notification,
+    const AssistantNotification& notification,
     int32_t action_index) {
   const std::string request_interaction =
       assistant::SerializeNotificationRequestInteraction(
-          notification->server_id, notification->consistency_token,
-          notification->opaque_token, action_index);
+          notification.server_id, notification.consistency_token,
+          notification.opaque_token, action_index);
 
   SendVoicelessInteraction(request_interaction,
                            /*description=*/"RequestNotification",
@@ -79,7 +79,7 @@
 }
 
 void ConversationController::DismissNotification(
-    mojom::AssistantNotificationPtr notification) {
+    const AssistantNotification& notification) {
   // |assistant_manager_internal()| may not exist if we are dismissing
   // notifications as part of a shutdown sequence.
   if (!assistant_manager_internal())
@@ -87,11 +87,11 @@
 
   const std::string dismissed_interaction =
       assistant::SerializeNotificationDismissedInteraction(
-          notification->server_id, notification->consistency_token,
-          notification->opaque_token, {notification->grouping_key});
+          notification.server_id, notification.consistency_token,
+          notification.opaque_token, {notification.grouping_key});
 
   assistant_client::VoicelessOptions options;
-  options.obfuscated_gaia_id = notification->obfuscated_gaia_id;
+  options.obfuscated_gaia_id = notification.obfuscated_gaia_id;
 
   assistant_manager_internal()->SendVoicelessInteraction(
       dismissed_interaction, /*description=*/"DismissNotification", options,
@@ -99,11 +99,11 @@
 }
 
 void ConversationController::SendAssistantFeedback(
-    mojom::AssistantFeedbackPtr feedback) {
-  std::string raw_image_data(feedback->screenshot_png.begin(),
-                             feedback->screenshot_png.end());
+    const AssistantFeedback& feedback) {
+  std::string raw_image_data(feedback.screenshot_png.begin(),
+                             feedback.screenshot_png.end());
   const std::string interaction = assistant::CreateSendFeedbackInteraction(
-      feedback->assistant_debug_info_allowed, feedback->description,
+      feedback.assistant_debug_info_allowed, feedback.description,
       raw_image_data);
 
   SendVoicelessInteraction(interaction,
diff --git a/chromeos/services/libassistant/conversation_controller.h b/chromeos/services/libassistant/conversation_controller.h
index 62e738c..8c6f628 100644
--- a/chromeos/services/libassistant/conversation_controller.h
+++ b/chromeos/services/libassistant/conversation_controller.h
@@ -23,6 +23,9 @@
 class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) ConversationController
     : public mojom::ConversationController {
  public:
+  using AssistantNotification = ::chromeos::assistant::AssistantNotification;
+  using AssistantFeedback = ::chromeos::assistant::AssistantFeedback;
+
   explicit ConversationController(ServiceController* service_controller);
   ConversationController(const ConversationController&) = delete;
   ConversationController& operator=(const ConversationController&) = delete;
@@ -36,10 +39,10 @@
       bool allow_tts,
       const base::Optional<std::string>& conversation_id) override;
   void StartEditReminderInteraction(const std::string& client_id) override;
-  void RetrieveNotification(mojom::AssistantNotificationPtr notification,
+  void RetrieveNotification(const AssistantNotification& notification,
                             int32_t action_index) override;
-  void DismissNotification(mojom::AssistantNotificationPtr) override;
-  void SendAssistantFeedback(mojom::AssistantFeedbackPtr feedback) override;
+  void DismissNotification(const AssistantNotification& notification) override;
+  void SendAssistantFeedback(const AssistantFeedback& feedback) override;
 
  private:
   void SendVoicelessInteraction(const std::string& interaction,
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc
index 00cfa21..974960b 100644
--- a/chromeos/services/libassistant/libassistant_service.cc
+++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -52,7 +52,6 @@
     platform_api_
         ->SetAudioInputProvider(
             &audio_input_controller_->audio_input_provider())
-        .SetAudioOutputProvider(&platform_api->GetAudioOutputProvider())
         .SetAuthProvider(fake_auth_provider_.get())
         .SetFileProvider(&platform_api->GetFileProvider())
         .SetNetworkProvider(&platform_api->GetNetworkProvider());
@@ -77,6 +76,7 @@
     mojo::PendingReceiver<mojom::DisplayController> display_controller,
     mojo::PendingReceiver<mojom::MediaController> media_controller,
     mojo::PendingReceiver<mojom::ServiceController> service_controller,
+    mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
     mojo::PendingRemote<mojom::MediaDelegate> media_delegate,
     mojo::PendingRemote<mojom::PlatformDelegate> platform_delegate) {
   platform_delegate_.Bind(std::move(platform_delegate));
@@ -86,9 +86,9 @@
   display_controller_->Bind(std::move(display_controller));
   media_controller_->Bind(std::move(media_controller),
                           std::move(media_delegate));
+  platform_api_->Bind(std::move(audio_output_delegate),
+                      platform_delegate_.get());
   service_controller_->Bind(std::move(service_controller));
-
-  platform_api_->Initialize(platform_delegate_.get());
 }
 
 void LibassistantService::SetInitializeCallback(InitializeCallback callback) {
diff --git a/chromeos/services/libassistant/libassistant_service.h b/chromeos/services/libassistant/libassistant_service.h
index 71e068bd2..f615684c 100644
--- a/chromeos/services/libassistant/libassistant_service.h
+++ b/chromeos/services/libassistant/libassistant_service.h
@@ -65,6 +65,7 @@
       mojo::PendingReceiver<mojom::DisplayController> display_controller,
       mojo::PendingReceiver<mojom::MediaController> media_controller,
       mojo::PendingReceiver<mojom::ServiceController> service_controller,
+      mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
       mojo::PendingRemote<mojom::MediaDelegate> media_delegate,
       mojo::PendingRemote<mojom::PlatformDelegate> platform_delegate) override;
   void AddSpeechRecognitionObserver(
diff --git a/chromeos/services/libassistant/platform_api.cc b/chromeos/services/libassistant/platform_api.cc
index 51625ca..847f9ba6 100644
--- a/chromeos/services/libassistant/platform_api.cc
+++ b/chromeos/services/libassistant/platform_api.cc
@@ -3,15 +3,21 @@
 // found in the LICENSE file.
 
 #include "chromeos/services/libassistant/platform_api.h"
+
 #include "base/check.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
+#include "chromeos/services/libassistant/audio/audio_output_provider_impl.h"
 #include "chromeos/services/libassistant/power_manager_provider_impl.h"
 #include "chromeos/services/libassistant/system_provider_impl.h"
+#include "media/audio/audio_device_description.h"
 
 namespace chromeos {
 namespace libassistant {
 
-PlatformApi::PlatformApi() {
+PlatformApi::PlatformApi()
+    : audio_output_provider_(std::make_unique<AudioOutputProviderImpl>(
+          /*background_task_runner=*/base::SequencedTaskRunnerHandle::Get(),
+          media::AudioDeviceDescription::kDefaultDeviceId)) {
   // Only enable native power features if they are supported by the UI.
   std::unique_ptr<PowerManagerProviderImpl> provider;
   if (assistant::features::IsPowerManagerEnabled()) {
@@ -22,9 +28,12 @@
 
 PlatformApi::~PlatformApi() = default;
 
-void PlatformApi::Initialize(
-    chromeos::libassistant::mojom::PlatformDelegate* delegate) {
-  system_provider_->Initialize(delegate);
+void PlatformApi::Bind(
+    mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
+    mojom::PlatformDelegate* platform_delegate) {
+  audio_output_provider_->Bind(std::move(audio_output_delegate),
+                               platform_delegate);
+  system_provider_->Initialize(platform_delegate);
 }
 
 PlatformApi& PlatformApi::SetAudioInputProvider(
@@ -33,12 +42,6 @@
   return *this;
 }
 
-PlatformApi& PlatformApi::SetAudioOutputProvider(
-    assistant_client::AudioOutputProvider* provider) {
-  audio_output_provider_ = provider;
-  return *this;
-}
-
 PlatformApi& PlatformApi::SetAuthProvider(
     assistant_client::AuthProvider* provider) {
   auth_provider_ = provider;
diff --git a/chromeos/services/libassistant/platform_api.h b/chromeos/services/libassistant/platform_api.h
index 86a035a..a473977 100644
--- a/chromeos/services/libassistant/platform_api.h
+++ b/chromeos/services/libassistant/platform_api.h
@@ -9,11 +9,14 @@
 
 #include <memory>
 
+#include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace chromeos {
 namespace libassistant {
 
+class AudioOutputProviderImpl;
 class SystemProviderImpl;
 
 // Implementation of the Libassistant PlatformApi.
@@ -26,10 +29,11 @@
   PlatformApi& operator=(const PlatformApi&) = delete;
   ~PlatformApi() override;
 
-  void Initialize(chromeos::libassistant::mojom::PlatformDelegate* delegate);
+  void Bind(
+      mojo::PendingRemote<mojom::AudioOutputDelegate> audio_output_delegate,
+      mojom::PlatformDelegate* platform_delegate);
 
   PlatformApi& SetAudioInputProvider(assistant_client::AudioInputProvider*);
-  PlatformApi& SetAudioOutputProvider(assistant_client::AudioOutputProvider*);
   PlatformApi& SetAuthProvider(assistant_client::AuthProvider*);
   PlatformApi& SetFileProvider(assistant_client::FileProvider*);
   PlatformApi& SetNetworkProvider(assistant_client::NetworkProvider*);
@@ -46,11 +50,11 @@
   // The below are all owned by the browser side |PlatformApiImpl|,
   // which outlives us.
   assistant_client::AudioInputProvider* audio_input_provider_ = nullptr;
-  assistant_client::AudioOutputProvider* audio_output_provider_ = nullptr;
   assistant_client::AuthProvider* auth_provider_ = nullptr;
   assistant_client::FileProvider* file_provider_ = nullptr;
   assistant_client::NetworkProvider* network_provider_ = nullptr;
 
+  std::unique_ptr<AudioOutputProviderImpl> audio_output_provider_;
   std::unique_ptr<SystemProviderImpl> system_provider_;
 };
 
diff --git a/chromeos/services/libassistant/public/mojom/BUILD.gn b/chromeos/services/libassistant/public/mojom/BUILD.gn
index 5d9d155..88169c61 100644
--- a/chromeos/services/libassistant/public/mojom/BUILD.gn
+++ b/chromeos/services/libassistant/public/mojom/BUILD.gn
@@ -41,11 +41,22 @@
           mojom = "chromeos.libassistant.mojom.AndroidAppStatus"
           cpp = "::chromeos::assistant::AppStatus"
         },
+        {
+          mojom = "chromeos.libassistant.mojom.AssistantNotification"
+          cpp = "::chromeos::assistant::AssistantNotification"
+        },
+        {
+          mojom = "chromeos.libassistant.mojom.AssistantFeedback"
+          cpp = "::chromeos::assistant::AssistantFeedback"
+        },
       ]
 
       traits_headers = [ "mojom_traits.h" ]
       traits_sources = [ "mojom_traits.cc" ]
-      traits_public_deps = [ "//chromeos/services/assistant/public/shared" ]
+      traits_public_deps = [
+        "//chromeos/services/assistant/public/cpp",
+        "//chromeos/services/assistant/public/shared",
+      ]
     },
   ]
 }
diff --git a/chromeos/services/libassistant/public/mojom/mojom_traits.cc b/chromeos/services/libassistant/public/mojom/mojom_traits.cc
index 1e11da4..bad44ac 100644
--- a/chromeos/services/libassistant/public/mojom/mojom_traits.cc
+++ b/chromeos/services/libassistant/public/mojom/mojom_traits.cc
@@ -9,7 +9,15 @@
 using AppStatus = chromeos::assistant::AppStatus;
 using AndroidAppStatus = chromeos::libassistant::mojom::AndroidAppStatus;
 using chromeos::assistant::AndroidAppInfo;
+using chromeos::assistant::AssistantFeedback;
+using chromeos::assistant::AssistantNotification;
 using chromeos::libassistant::mojom::AndroidAppInfoDataView;
+using chromeos::libassistant::mojom::AssistantFeedbackDataView;
+using chromeos::libassistant::mojom::AssistantNotificationDataView;
+
+////////////////////////////////////////////////////////////////////////////////
+// AndroidAppStatus
+////////////////////////////////////////////////////////////////////////////////
 
 AndroidAppStatus EnumTraits<AndroidAppStatus, AppStatus>::ToMojom(
     AppStatus input) {
@@ -49,7 +57,12 @@
   return true;
 }
 
-std::string StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::package_name(
+////////////////////////////////////////////////////////////////////////////////
+// AndroidAppInfo
+////////////////////////////////////////////////////////////////////////////////
+
+const std::string&
+StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::package_name(
     const AndroidAppInfo& input) {
   return input.package_name;
 }
@@ -59,13 +72,13 @@
   return input.version;
 }
 
-std::string
+const std::string&
 StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::localized_app_name(
     const AndroidAppInfo& input) {
   return input.localized_app_name;
 }
 
-std::string StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::intent(
+const std::string& StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::intent(
     const AndroidAppInfo& input) {
   return input.intent;
 }
@@ -76,7 +89,7 @@
   return input.status;
 }
 
-std::string StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::action(
+const std::string& StructTraits<AndroidAppInfoDataView, AndroidAppInfo>::action(
     const AndroidAppInfo& input) {
   return input.action;
 }
@@ -98,4 +111,86 @@
   return true;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// AssistantNotification
+////////////////////////////////////////////////////////////////////////////////
+
+const std::string&
+StructTraits<AssistantNotificationDataView, AssistantNotification>::server_id(
+    const AssistantNotification& input) {
+  return input.server_id;
+}
+
+const std::string&
+StructTraits<AssistantNotificationDataView, AssistantNotification>::
+    consistency_token(const AssistantNotification& input) {
+  return input.consistency_token;
+}
+
+const std::string& StructTraits<
+    AssistantNotificationDataView,
+    AssistantNotification>::opaque_token(const AssistantNotification& input) {
+  return input.opaque_token;
+}
+
+const std::string& StructTraits<
+    AssistantNotificationDataView,
+    AssistantNotification>::grouping_key(const AssistantNotification& input) {
+  return input.grouping_key;
+}
+
+const std::string&
+StructTraits<AssistantNotificationDataView, AssistantNotification>::
+    obfuscated_gaia_id(const AssistantNotification& input) {
+  return input.obfuscated_gaia_id;
+}
+
+bool StructTraits<AssistantNotificationDataView, AssistantNotification>::Read(
+    chromeos::libassistant::mojom::AssistantNotificationDataView data,
+    AssistantNotification* output) {
+  if (!data.ReadServerId(&output->server_id))
+    return false;
+  if (!data.ReadConsistencyToken(&output->consistency_token))
+    return false;
+  if (!data.ReadOpaqueToken(&output->opaque_token))
+    return false;
+  if (!data.ReadGroupingKey(&output->grouping_key))
+    return false;
+  if (!data.ReadObfuscatedGaiaId(&output->obfuscated_gaia_id))
+    return false;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AssistantFeedback
+////////////////////////////////////////////////////////////////////////////////
+
+const std::string&
+StructTraits<AssistantFeedbackDataView, AssistantFeedback>::description(
+    const AssistantFeedback& input) {
+  return input.description;
+}
+
+bool StructTraits<AssistantFeedbackDataView, AssistantFeedback>::
+    assistant_debug_info_allowed(const AssistantFeedback& input) {
+  return input.assistant_debug_info_allowed;
+}
+
+base::span<const uint8_t>
+StructTraits<AssistantFeedbackDataView, AssistantFeedback>::screenshot_png(
+    const AssistantFeedback& input) {
+  return input.screenshot_png;
+}
+
+bool StructTraits<AssistantFeedbackDataView, AssistantFeedback>::Read(
+    chromeos::libassistant::mojom::AssistantFeedbackDataView data,
+    AssistantFeedback* output) {
+  if (!data.ReadDescription(&output->description))
+    return false;
+  output->assistant_debug_info_allowed = data.assistant_debug_info_allowed();
+  if (!data.ReadScreenshotPng(&output->screenshot_png))
+    return false;
+  return true;
+}
+
 }  // namespace mojo
diff --git a/chromeos/services/libassistant/public/mojom/mojom_traits.h b/chromeos/services/libassistant/public/mojom/mojom_traits.h
index d544d726..3bee4413 100644
--- a/chromeos/services/libassistant/public/mojom/mojom_traits.h
+++ b/chromeos/services/libassistant/public/mojom/mojom_traits.h
@@ -5,8 +5,14 @@
 #ifndef CHROMEOS_SERVICES_LIBASSISTANT_PUBLIC_MOJOM_MOJOM_TRAITS_H_
 #define CHROMEOS_SERVICES_LIBASSISTANT_PUBLIC_MOJOM_MOJOM_TRAITS_H_
 
+#include <cstdint>
+
+#include "base/containers/span.h"
+#include "chromeos/services/assistant/public/cpp/assistant_notification.h"
+#include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/assistant/public/shared/utils.h"
-#include "chromeos/services/libassistant/public/mojom/android_app_info.mojom.h"
+#include "chromeos/services/libassistant/public/mojom/android_app_info.mojom-shared.h"
+#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom-shared.h"
 
 namespace mojo {
 
@@ -15,12 +21,12 @@
                     chromeos::assistant::AndroidAppInfo> {
   using AndroidAppInfo = chromeos::assistant::AndroidAppInfo;
 
-  static std::string package_name(const AndroidAppInfo& input);
+  static const std::string& package_name(const AndroidAppInfo& input);
   static int64_t version(const AndroidAppInfo& input);
-  static std::string localized_app_name(const AndroidAppInfo& input);
-  static std::string intent(const AndroidAppInfo& input);
+  static const std::string& localized_app_name(const AndroidAppInfo& input);
+  static const std::string& intent(const AndroidAppInfo& input);
   static chromeos::assistant::AppStatus status(const AndroidAppInfo& input);
-  static std::string action(const AndroidAppInfo& input);
+  static const std::string& action(const AndroidAppInfo& input);
 
   static bool Read(chromeos::libassistant::mojom::AndroidAppInfoDataView data,
                    AndroidAppInfo* output);
@@ -35,6 +41,40 @@
   static bool FromMojom(AndroidAppStatus input, AppStatus* output);
 };
 
+template <>
+struct StructTraits<
+    chromeos::libassistant::mojom::AssistantNotificationDataView,
+    chromeos::assistant::AssistantNotification> {
+  using AssistantNotification = chromeos::assistant::AssistantNotification;
+
+  static const std::string& server_id(const AssistantNotification& input);
+  static const std::string& consistency_token(
+      const AssistantNotification& input);
+  static const std::string& opaque_token(const AssistantNotification& input);
+  static const std::string& grouping_key(const AssistantNotification& input);
+  static const std::string& obfuscated_gaia_id(
+      const AssistantNotification& input);
+
+  static bool Read(
+      chromeos::libassistant::mojom::AssistantNotificationDataView data,
+      AssistantNotification* output);
+};
+
+template <>
+struct StructTraits<chromeos::libassistant::mojom::AssistantFeedbackDataView,
+                    chromeos::assistant::AssistantFeedback> {
+  using AssistantFeedback = chromeos::assistant::AssistantFeedback;
+
+  static const std::string& description(const AssistantFeedback& input);
+  static bool assistant_debug_info_allowed(const AssistantFeedback& input);
+  static base::span<const uint8_t> screenshot_png(
+      const AssistantFeedback& input);
+
+  static bool Read(
+      chromeos::libassistant::mojom::AssistantFeedbackDataView data,
+      AssistantFeedback* output);
+};
+
 }  // namespace mojo
 
 #endif  // CHROMEOS_SERVICES_LIBASSISTANT_PUBLIC_MOJOM_MOJOM_TRAITS_H_
diff --git a/chromeos/services/libassistant/public/mojom/service.mojom b/chromeos/services/libassistant/public/mojom/service.mojom
index c21d2da7..605dfa1 100644
--- a/chromeos/services/libassistant/public/mojom/service.mojom
+++ b/chromeos/services/libassistant/public/mojom/service.mojom
@@ -6,6 +6,7 @@
 
 import "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom";
 import "chromeos/services/libassistant/public/mojom/conversation_controller.mojom";
+import "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom";
 import "chromeos/services/libassistant/public/mojom/display_controller.mojom";
 import "chromeos/services/libassistant/public/mojom/platform_delegate.mojom";
 import "chromeos/services/libassistant/public/mojom/service_controller.mojom";
@@ -27,6 +28,7 @@
     pending_receiver<DisplayController> display_controller,
     pending_receiver<MediaController> media_controller,
     pending_receiver<ServiceController> service_controller,
+    pending_remote<AudioOutputDelegate> audio_output_delegate,
     pending_remote<MediaDelegate> media_delegate,
     pending_remote<PlatformDelegate> platform_delegate
   );
diff --git a/chromeos/services/libassistant/test_support/libassistant_service_tester.cc b/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
index 6738dab..46b4310 100644
--- a/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
+++ b/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
@@ -36,19 +36,24 @@
 }
 
 void LibassistantServiceTester::BindControllers() {
-  mojo::PendingRemote<mojom::PlatformDelegate> pending_platform_delegate_remote;
-  pending_platform_delegate_ =
-      pending_platform_delegate_remote.InitWithNewPipeAndPassReceiver();
-
+  mojo::PendingRemote<mojom::AudioOutputDelegate>
+      pending_audio_output_delegate_remote;
   mojo::PendingRemote<mojom::MediaDelegate> pending_media_delegate_remote;
+  mojo::PendingRemote<mojom::PlatformDelegate> pending_platform_delegate_remote;
+
+  pending_audio_output_delegate_ =
+      pending_audio_output_delegate_remote.InitWithNewPipeAndPassReceiver();
   pending_media_delegate_ =
       pending_media_delegate_remote.InitWithNewPipeAndPassReceiver();
+  pending_platform_delegate_ =
+      pending_platform_delegate_remote.InitWithNewPipeAndPassReceiver();
 
   service_.Bind(audio_input_controller_.BindNewPipeAndPassReceiver(),
                 conversation_controller_.BindNewPipeAndPassReceiver(),
                 display_controller_.BindNewPipeAndPassReceiver(),
                 media_controller_.BindNewPipeAndPassReceiver(),
                 service_controller_.BindNewPipeAndPassReceiver(),
+                std::move(pending_audio_output_delegate_remote),
                 std::move(pending_media_delegate_remote),
                 std::move(pending_platform_delegate_remote));
 }
diff --git a/chromeos/services/libassistant/test_support/libassistant_service_tester.h b/chromeos/services/libassistant/test_support/libassistant_service_tester.h
index 7d377b65..c5920ef 100644
--- a/chromeos/services/libassistant/test_support/libassistant_service_tester.h
+++ b/chromeos/services/libassistant/test_support/libassistant_service_tester.h
@@ -8,6 +8,7 @@
 #include "chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h"
 #include "chromeos/services/libassistant/libassistant_service.h"
 #include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
+#include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
 #include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/display_controller.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/service.mojom.h"
@@ -68,8 +69,10 @@
   mojo::Remote<mojom::DisplayController> display_controller_;
   mojo::Remote<mojom::MediaController> media_controller_;
   mojo::Remote<mojom::ServiceController> service_controller_;
-  mojo::PendingReceiver<mojom::PlatformDelegate> pending_platform_delegate_;
+  mojo::PendingReceiver<mojom::AudioOutputDelegate>
+      pending_audio_output_delegate_;
   mojo::PendingReceiver<mojom::MediaDelegate> pending_media_delegate_;
+  mojo::PendingReceiver<mojom::PlatformDelegate> pending_platform_delegate_;
 
   mojo::Remote<mojom::LibassistantService> service_remote_;
   assistant::FakeAssistantManagerServiceDelegate
diff --git a/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc b/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
index c452224..00dcd31 100644
--- a/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
@@ -198,7 +198,12 @@
   EXPECT_TRUE(model.is_bound());
 }
 
-class TestSodaClient : public mojom::SodaClient {};
+class TestSodaClient : public mojom::SodaClient {
+  void OnStop() override {}
+  void OnStart() override {}
+  void OnSpeechRecognizerEvent(mojom::SpeechRecognizerEventPtr event) override {
+  }
+};
 
 // Tests that LoadSpeechRecognizer runs OK without a crash in a basic Mojo
 // Environment.
diff --git a/chromeos/services/machine_learning/public/mojom/soda.mojom b/chromeos/services/machine_learning/public/mojom/soda.mojom
index a1198d37..aa99605 100644
--- a/chromeos/services/machine_learning/public/mojom/soda.mojom
+++ b/chromeos/services/machine_learning/public/mojom/soda.mojom
@@ -129,6 +129,14 @@
 // the client, SODA then calls these as 'events' with appropriate details
 // when recognition occurs.
 interface SodaClient {
+  // After SODA successfully starts / stops, in case the client
+  // cares:
+  OnStart();
+  OnStop();
+
+  // This is how the client receives actual recognized text as well as other
+  // conclusions from the SODA model like "speech ended".
+  OnSpeechRecognizerEvent(SpeechRecognizerEvent event);
 };
 
 // The mojom interface for performing the recognition of handwritten text.
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index fc4d1e85..e864a19f 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -8,10 +8,12 @@
 
 #include "base/optional.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chromeos/components/sync_wifi/network_eligibility_checker.h"
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
 #include "chromeos/dbus/hermes/hermes_manager_client.h"
 #include "chromeos/login/login_state/login_state.h"
+#include "chromeos/network/cellular_esim_profile_handler.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_connection_handler.h"
@@ -298,8 +300,51 @@
   return result;
 }
 
+base::Optional<std::string> GetESimProfileName(
+    CellularESimProfileHandler* cellular_esim_profile_handler,
+    const NetworkState* network_state) {
+  DCHECK(network_state);
+
+  // CellularESimProfileHandler is not available if the relevant flag is
+  // disabled.
+  if (!cellular_esim_profile_handler)
+    return base::nullopt;
+
+  // Only Cellular networks correspond to eSIM profiles.
+  if (network_state->type() != shill::kTypeCellular)
+    return base::nullopt;
+
+  // eSIM profiles have an associated EID and ICCID.
+  if (network_state->eid().empty() || network_state->iccid().empty())
+    return base::nullopt;
+
+  std::vector<CellularESimProfile> profiles =
+      cellular_esim_profile_handler->GetESimProfiles();
+  for (const auto& profile : profiles) {
+    if (profile.eid() != network_state->eid() ||
+        profile.iccid() != network_state->iccid()) {
+      continue;
+    }
+
+    // We've found a profile corresponding to the network. If possible, use the
+    // profile's nickname, falling back to the name or the service provider.
+
+    if (!profile.nickname().empty())
+      return base::UTF16ToUTF8(profile.nickname());
+
+    if (!profile.name().empty())
+      return base::UTF16ToUTF8(profile.name());
+
+    if (!profile.service_provider().empty())
+      return base::UTF16ToUTF8(profile.service_provider());
+  }
+
+  return base::nullopt;
+}
+
 mojom::NetworkStatePropertiesPtr NetworkStateToMojo(
     NetworkStateHandler* network_state_handler,
+    CellularESimProfileHandler* cellular_esim_profile_handler,
     const std::vector<mojom::VpnProviderPtr>& vpn_providers,
     const NetworkState* network) {
   mojom::NetworkType type = ShillTypeToMojo(network->type());
@@ -349,6 +394,11 @@
 
   switch (type) {
     case mojom::NetworkType::kCellular: {
+      base::Optional<std::string> profile_name =
+          GetESimProfileName(cellular_esim_profile_handler, network);
+      if (profile_name)
+        result->name = *profile_name;
+
       auto cellular = mojom::CellularStateProperties::New();
       cellular->activation_state = network->GetMojoActivationState();
       cellular->network_technology = ShillToOnc(network->network_technology(),
@@ -1772,6 +1822,7 @@
     : CrosNetworkConfig(
           NetworkHandler::Get()->network_state_handler(),
           NetworkHandler::Get()->network_device_handler(),
+          NetworkHandler::Get()->cellular_esim_profile_handler(),
           NetworkHandler::Get()->managed_network_configuration_handler(),
           NetworkHandler::Get()->network_connection_handler(),
           NetworkHandler::Get()->network_certificate_handler()) {}
@@ -1779,11 +1830,13 @@
 CrosNetworkConfig::CrosNetworkConfig(
     NetworkStateHandler* network_state_handler,
     NetworkDeviceHandler* network_device_handler,
+    CellularESimProfileHandler* cellular_esim_profile_handler,
     ManagedNetworkConfigurationHandler* network_configuration_handler,
     NetworkConnectionHandler* network_connection_handler,
     NetworkCertificateHandler* network_certificate_handler)
     : network_state_handler_(network_state_handler),
       network_device_handler_(network_device_handler),
+      cellular_esim_profile_handler_(cellular_esim_profile_handler),
       network_configuration_handler_(network_configuration_handler),
       network_connection_handler_(network_connection_handler),
       network_certificate_handler_(network_certificate_handler) {
@@ -1830,8 +1883,9 @@
     std::move(callback).Run(nullptr);
     return;
   }
-  std::move(callback).Run(
-      NetworkStateToMojo(network_state_handler_, vpn_providers_, network));
+  std::move(callback).Run(NetworkStateToMojo(network_state_handler_,
+                                             cellular_esim_profile_handler_,
+                                             vpn_providers_, network));
 }
 
 void CrosNetworkConfig::GetNetworkStateList(
@@ -1869,8 +1923,9 @@
       // represent a separate network service.
       continue;
     }
-    mojom::NetworkStatePropertiesPtr mojo_network =
-        NetworkStateToMojo(network_state_handler_, vpn_providers_, network);
+    mojom::NetworkStatePropertiesPtr mojo_network = NetworkStateToMojo(
+        network_state_handler_, cellular_esim_profile_handler_, vpn_providers_,
+        network);
     if (mojo_network)
       result.emplace_back(std::move(mojo_network));
   }
@@ -2664,8 +2719,9 @@
     const std::vector<const NetworkState*>& active_networks) {
   std::vector<mojom::NetworkStatePropertiesPtr> result;
   for (const NetworkState* network : active_networks) {
-    mojom::NetworkStatePropertiesPtr mojo_network =
-        NetworkStateToMojo(network_state_handler_, vpn_providers_, network);
+    mojom::NetworkStatePropertiesPtr mojo_network = NetworkStateToMojo(
+        network_state_handler_, cellular_esim_profile_handler_, vpn_providers_,
+        network);
     if (mojo_network)
       result.emplace_back(std::move(mojo_network));
   }
@@ -2677,7 +2733,8 @@
   if (network->type() == shill::kTypeEthernetEap)
     return;
   mojom::NetworkStatePropertiesPtr mojo_network =
-      NetworkStateToMojo(network_state_handler_, vpn_providers_, network);
+      NetworkStateToMojo(network_state_handler_, cellular_esim_profile_handler_,
+                         vpn_providers_, network);
   if (!mojo_network)
     return;
   for (auto& observer : observers_)
diff --git a/chromeos/services/network_config/cros_network_config.h b/chromeos/services/network_config/cros_network_config.h
index 3ffbc76..a6b3a74 100644
--- a/chromeos/services/network_config/cros_network_config.h
+++ b/chromeos/services/network_config/cros_network_config.h
@@ -21,6 +21,7 @@
 
 namespace chromeos {
 
+class CellularESimProfileHandler;
 class ManagedNetworkConfigurationHandler;
 class NetworkConnectionHandler;
 class NetworkDeviceHandler;
@@ -41,6 +42,7 @@
   CrosNetworkConfig(
       NetworkStateHandler* network_state_handler,
       NetworkDeviceHandler* network_device_handler,
+      CellularESimProfileHandler* cellular_esim_profile_handler,
       ManagedNetworkConfigurationHandler* network_configuration_handler,
       NetworkConnectionHandler* network_connection_handler,
       NetworkCertificateHandler* network_certificate_handler);
@@ -160,6 +162,7 @@
 
   NetworkStateHandler* network_state_handler_;    // Unowned
   NetworkDeviceHandler* network_device_handler_;  // Unowned
+  CellularESimProfileHandler* cellular_esim_profile_handler_;  // Unowned
   ManagedNetworkConfigurationHandler*
       network_configuration_handler_;                       // Unowned
   NetworkConnectionHandler* network_connection_handler_;    // Unowned
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 7f223a9..c4d0264 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -27,6 +27,7 @@
 #include "chromeos/network/onc/onc_utils.h"
 #include "chromeos/network/prohibited_technologies_handler.h"
 #include "chromeos/network/proxy/ui_proxy_config_service.h"
+#include "chromeos/network/test_cellular_esim_profile_handler.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-shared.h"
 #include "components/onc/onc_constants.h"
@@ -96,6 +97,9 @@
             helper_.network_state_handler(), network_profile_handler_.get(),
             network_device_handler_.get(), network_configuration_handler_.get(),
             ui_proxy_config_service_.get());
+    cellular_esim_profile_handler_ =
+        std::make_unique<TestCellularESimProfileHandler>();
+    cellular_esim_profile_handler_->Init();
     network_connection_handler_ =
         NetworkConnectionHandler::InitializeForTesting(
             helper_.network_state_handler(),
@@ -106,6 +110,7 @@
         std::make_unique<NetworkCertificateHandler>();
     cros_network_config_ = std::make_unique<CrosNetworkConfig>(
         helper_.network_state_handler(), network_device_handler_.get(),
+        cellular_esim_profile_handler_.get(),
         managed_network_configuration_handler_.get(),
         network_connection_handler_.get(), network_certificate_handler_.get());
     SetupPolicy();
@@ -523,6 +528,8 @@
   std::unique_ptr<NetworkConfigurationHandler> network_configuration_handler_;
   std::unique_ptr<ManagedNetworkConfigurationHandler>
       managed_network_configuration_handler_;
+  std::unique_ptr<TestCellularESimProfileHandler>
+      cellular_esim_profile_handler_;
   std::unique_ptr<NetworkConnectionHandler> network_connection_handler_;
   std::unique_ptr<chromeos::UIProxyConfigService> ui_proxy_config_service_;
   TestingPrefServiceSimple local_state_;
@@ -657,6 +664,40 @@
   EXPECT_EQ("wifi3_guid", networks[2]->guid);
 }
 
+TEST_F(CrosNetworkConfigTest, ESimNetworkNameComesFromHermes) {
+  const char kTestEuiccPath[] = "euicc_path";
+  const char kTestProfileServicePath[] = "esim_service_path";
+  const char kTestIccid[] = "iccid";
+
+  const char kTestProfileName[] = "test_profile_name";
+  const char kTestNameFromShill[] = "shill_network_name";
+
+  // Add a fake eSIM with name kTestProfileName.
+  helper().hermes_manager_test()->AddEuicc(dbus::ObjectPath(kTestEuiccPath),
+                                           "eid", true);
+  helper().hermes_euicc_test()->AddCarrierProfile(
+      dbus::ObjectPath(kTestProfileServicePath),
+      dbus::ObjectPath(kTestEuiccPath), kTestIccid, kTestProfileName,
+      "service_provider", "activation_code", kTestProfileServicePath,
+      hermes::profile::State::kInactive,
+      /*service_only=*/false);
+  base::RunLoop().RunUntilIdle();
+
+  // Change the network's name in Shill. Now, Hermes and Shill have different
+  // names associated with the profile.
+  helper().SetServiceProperty(kTestProfileServicePath, shill::kNameProperty,
+                              base::Value(kTestNameFromShill));
+  base::RunLoop().RunUntilIdle();
+
+  // Fetch the Cellular network for the eSIM profile.
+  std::string esim_guid = std::string("esim_guid") + kTestIccid;
+  mojom::NetworkStatePropertiesPtr network = GetNetworkState(esim_guid);
+
+  // The network's name should be the profile name (from Hermes), not the name
+  // from Shill.
+  EXPECT_EQ(kTestProfileName, network->name);
+}
+
 TEST_F(CrosNetworkConfigTest, GetDeviceStateList) {
   std::vector<mojom::DeviceStatePropertiesPtr> devices = GetDeviceStateList();
   ASSERT_EQ(4u, devices.size());
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc b/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
index 5e2ad1fc..8b0102ca 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
@@ -32,6 +32,7 @@
     cros_network_config_impl_ = std::make_unique<CrosNetworkConfig>(
         network_state_helper_.network_state_handler(),
         network_state_helper_.network_device_handler(),
+        /*cellular_esim_profile_handler=*/nullptr,
         network_configuration_handler,
         /*network_connection_handler=*/nullptr,
         /*network_certificate_handler=*/nullptr);
diff --git a/chromeos/settings/system_settings_provider.cc b/chromeos/settings/system_settings_provider.cc
index 4cd9393..127cebd 100644
--- a/chromeos/settings/system_settings_provider.cc
+++ b/chromeos/settings/system_settings_provider.cc
@@ -15,7 +15,7 @@
 namespace chromeos {
 namespace {
 // TODO(olsen): PerUserTimeZoneEnabled and FineGrainedTimeZoneDetectionEnabled
-// are duplicated in chrome/browser/chromeos/system/timezone_util.cc, which
+// are duplicated in chrome/browser/ash/system/timezone_util.cc, which
 // is not visible from this package. Try to re-unify these functions by moving
 // timezone_util to src/chromeos too (out of src/chrome/browser).
 
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index 39f328c..24257c3 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -39,6 +39,12 @@
 const base::Feature kEnableUnifiedAudioFocusFeature{
     "ArcEnableUnifiedAudioFocus", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Controls ARC Unspecialized Application Processes.
+// When enabled, Android creates a pool of processes
+// that will start applications so that zygote doesn't have to wake.
+const base::Feature kEnableUsap{"ArcEnableUsap",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls experimental file picker feature for ARC.
 const base::Feature kFilePickerExperimentFeature{
     "ArcFilePickerExperiment", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h
index 6c0e595..8efc6cc 100644
--- a/components/arc/arc_features.h
+++ b/components/arc/arc_features.h
@@ -19,6 +19,7 @@
 extern const base::Feature kEnableDocumentsProviderInFilesAppFeature;
 extern const base::Feature kEnableRegularToChildTransitionFeature;
 extern const base::Feature kEnableUnifiedAudioFocusFeature;
+extern const base::Feature kEnableUsap;
 extern const base::Feature kFilePickerExperimentFeature;
 extern const base::Feature kNativeBridge64BitSupportExperimentFeature;
 extern const base::Feature kNativeBridgeToggleFeature;
diff --git a/components/arc/session/arc_container_client_adapter.cc b/components/arc/session/arc_container_client_adapter.cc
index 99e89159..d6a3354 100644
--- a/components/arc/session/arc_container_client_adapter.cc
+++ b/components/arc/session/arc_container_client_adapter.cc
@@ -119,6 +119,17 @@
     request.set_disable_media_store_maintenance(
         params.disable_media_store_maintenance);
     request.set_arc_generate_pai(params.arc_generate_play_auto_install);
+
+    switch (params.usap_profile) {
+      case StartParams::UsapProfile::DEFAULT:
+        break;
+      case StartParams::UsapProfile::M4G:
+      case StartParams::UsapProfile::M8G:
+      case StartParams::UsapProfile::M16G:
+        VLOG(1) << "USAP profile is not supported for container.";
+        break;
+    }
+
     chromeos::SessionManagerClient::Get()->StartArcMiniContainer(
         request, std::move(callback));
   }
diff --git a/components/arc/session/arc_session_impl.cc b/components/arc/session/arc_session_impl.cc
index d26a89d..7b2981b 100644
--- a/components/arc/session/arc_session_impl.cc
+++ b/components/arc/session/arc_session_impl.cc
@@ -130,6 +130,39 @@
           << (mem_info.total / 1024) << "Mb device.";
 }
 
+// Applies USAP profile to the ARC mini instance start params.
+// Profile is determined based on enable feature and available memory on the
+// device. Possible profiles 16G,8G and 4G. For low memory devices USAP
+// profile is not overridden. If |memory_stat_file_for_testing| is set,
+// it specifies the file to read in tests instead of /proc/meminfo in
+// production.
+// Note: This is only used for VM. This profile does nothing for container.
+void ApplyUsapProfile(
+    ArcSessionImpl::SystemMemoryInfoCallback system_memory_info_callback,
+    StartParams* params) {
+  // Check if enabled.
+  if (!base::FeatureList::IsEnabled(arc::kEnableUsap)) {
+    VLOG(1) << "USAP profile is not enabled.";
+    return;
+  }
+
+  base::SystemMemoryInfoKB mem_info;
+  if (!system_memory_info_callback.Run(&mem_info)) {
+    LOG(ERROR) << "Failed to get system memory info";
+    return;
+  }
+
+  if (mem_info.total >= kClassify16GbDeviceInKb) {
+    params->usap_profile = StartParams::UsapProfile::M16G;
+  } else if (mem_info.total >= kClassify8GbDeviceInKb) {
+    params->usap_profile = StartParams::UsapProfile::M8G;
+  } else if (mem_info.total >= kClassify4GbDeviceInKb) {
+    params->usap_profile = StartParams::UsapProfile::M4G;
+  } else {
+    params->usap_profile = StartParams::UsapProfile::DEFAULT;
+  }
+}
+
 // Real Delegate implementation to connect Mojo.
 class ArcSessionDelegateImpl : public ArcSessionImpl::Delegate {
  public:
@@ -495,6 +528,7 @@
           << ", num_cores_disabled=" << params.num_cores_disabled;
 
   ApplyDalvikMemoryProfile(system_memory_info_callback_, &params);
+  ApplyUsapProfile(system_memory_info_callback_, &params);
 
   client_->StartMiniArc(std::move(params),
                         base::BindOnce(&ArcSessionImpl::OnMiniInstanceStarted,
diff --git a/components/arc/session/arc_start_params.h b/components/arc/session/arc_start_params.h
index 0ed94c5..6b6ce19 100644
--- a/components/arc/session/arc_start_params.h
+++ b/components/arc/session/arc_start_params.h
@@ -33,6 +33,17 @@
     M16G,
   };
 
+  enum class UsapProfile {
+    // Default USAP profile suitable for all devices.
+    DEFAULT = 0,
+    // USAP profile suitable for 4G devices.
+    M4G,
+    // USAP profile suitable for 8G devices.
+    M8G,
+    // USAP profile suitable for 16G devices.
+    M16G,
+  };
+
   StartParams();
   ~StartParams();
   StartParams(StartParams&& other);
@@ -50,6 +61,8 @@
 
   DalvikMemoryProfile dalvik_memory_profile = DalvikMemoryProfile::DEFAULT;
 
+  UsapProfile usap_profile = UsapProfile::DEFAULT;
+
   // Experiment flag for ARC Custom Tabs.
   bool arc_custom_tabs_experiment = false;
 
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index 2db771a..420277b4 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -273,6 +273,25 @@
       break;
   }
 
+  std::string log_profile_name;
+  switch (start_params.usap_profile) {
+    case StartParams::UsapProfile::DEFAULT:
+      log_profile_name = "default low-memory";
+      break;
+    case StartParams::UsapProfile::M4G:
+      result.push_back("androidboot.usap_profile=4G");
+      log_profile_name = "high-memory 4G";
+      break;
+    case StartParams::UsapProfile::M8G:
+      result.push_back("androidboot.usap_profile=8G");
+      log_profile_name = "high-memory 8G";
+      break;
+    case StartParams::UsapProfile::M16G:
+      result.push_back("androidboot.usap_profile=16G");
+      log_profile_name = "high-memory 16G";
+      break;
+  }
+  VLOG(1) << "Applied " << log_profile_name << " USAP profile";
   return result;
 }
 
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 2209848..77bc39a8 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -1666,5 +1666,44 @@
   }
 }
 
+struct UsapProfileTestParam {
+  // Requested profile.
+  StartParams::UsapProfile profile;
+  // Name of profile that is expected.
+  const char* profile_name;
+};
+
+constexpr UsapProfileTestParam kUsapProfileTestCases[] = {
+    {StartParams::UsapProfile::DEFAULT, nullptr},
+    {StartParams::UsapProfile::M4G, "4G"},
+    {StartParams::UsapProfile::M8G, "8G"},
+    {StartParams::UsapProfile::M16G, "16G"}};
+
+class ArcVmClientAdapterUsapProfileTest
+    : public ArcVmClientAdapterTest,
+      public testing::WithParamInterface<UsapProfileTestParam> {};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         ArcVmClientAdapterUsapProfileTest,
+                         ::testing::ValuesIn(kUsapProfileTestCases));
+
+TEST_P(ArcVmClientAdapterUsapProfileTest, Profile) {
+  const auto& test_param = GetParam();
+  StartParams start_params(GetPopulatedStartParams());
+  start_params.usap_profile = test_param.profile;
+  SetValidUserInfo();
+  StartMiniArcWithParams(true, std::move(start_params));
+  auto request = GetTestConciergeClient()->start_arc_vm_request();
+  if (test_param.profile_name) {
+    EXPECT_TRUE(base::Contains(
+        GetTestConciergeClient()->start_arc_vm_request().params(),
+        std::string("androidboot.usap_profile=") + test_param.profile_name));
+  } else {
+    // Not expected any arc_dalvik_memory_profile.
+    for (const auto& param : request.params())
+      EXPECT_EQ(std::string::npos, param.find("usap_profile"));
+  }
+}
+
 }  // namespace
 }  // namespace arc
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index e13c708..5101799 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -17,6 +17,7 @@
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/payments/payments_service_url.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_context.h"
@@ -348,6 +349,8 @@
     return;
   }
 
+  ShowOfferNotificationIfApplicable(navigation_handle);
+
   submitted_forms_.clear();
   autofill_handler_->Reset();
 }
@@ -459,4 +462,28 @@
   autofill_manager_ = nullptr;
 }
 
+void ContentAutofillDriver::ShowOfferNotificationIfApplicable(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame())
+    return;
+
+  // TODO(crbug.com/1093057): Android webview does not have |autofill_manager_|,
+  // so flow is not enabled in Android Webview.
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillEnableOfferNotification) ||
+      !autofill_manager_) {
+    return;
+  }
+
+  AutofillOfferManager* offer_manager = autofill_manager_->offer_manager();
+  // Try to show offer notification when the last committed URL has the domain
+  // that an offer is applicable for.
+  GURL url = autofill_manager_->client()->GetLastCommittedURL();
+  if (offer_manager->IsUrlEligible(url)) {
+    std::vector<GURL> domains =
+        offer_manager->GetEligibleDomainsForOfferForUrl(url);
+    autofill_manager_->client()->ShowOfferNotificationIfApplicable(domains);
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 8d01a26..f659b5d 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -189,6 +189,11 @@
   // been used.
   bool DocumentUsedWebOTP() const;
 
+  // Show a bubble or infobar indicating that the current page has an eligible
+  // offer or reward, if the bubble/infobar is not currently being visible.
+  void ShowOfferNotificationIfApplicable(
+      content::NavigationHandle* navigation_handle);
+
   // Weak ref to the RenderFrameHost the driver is associated with. Should
   // always be non-NULL and valid for lifetime of |this|.
   content::RenderFrameHost* const render_frame_host_;
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index 94e37d0..0a95921 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -57,6 +57,12 @@
 }
 #endif
 
+void AutofillClient::ShowOfferNotificationIfApplicable(
+    const std::vector<GURL>& domains_to_display_bubble) {
+  // This is overridden by platform subclasses. Currently only
+  // ChromeAutofillClient (Chrome Desktop and Clank) implement this.
+}
+
 LogManager* AutofillClient::GetLogManager() const {
   return nullptr;
 }
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 8b9107c2..0dd96da 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -502,6 +502,16 @@
   // Hide the Autofill popup if one is currently showing.
   virtual void HideAutofillPopup(PopupHidingReason reason) = 0;
 
+  // TODO(crbug.com/1093057): Rename all the "domain" in this flow to origin.
+  //                          The server is passing down full origin of the
+  //                          urls. "Domain" is no longer accurate.
+  // Will show a bubble or infobar indicating that the current web domain has an
+  // eligible offer or reward if no other notification bubble is currently
+  // visible. See bubble controller for details. The bubble is sticky over a set
+  // of domains given in |domains_to_display_bubble|.
+  virtual void ShowOfferNotificationIfApplicable(
+      const std::vector<GURL>& domains_to_display_bubble);
+
   // Whether the Autocomplete feature of Autofill should be enabled.
   virtual bool IsAutocompleteEnabled() = 0;
 
diff --git a/components/autofill/core/browser/autofill_handler.cc b/components/autofill/core/browser/autofill_handler.cc
index 4dacf97..a744f8c5 100644
--- a/components/autofill/core/browser/autofill_handler.cc
+++ b/components/autofill/core/browser/autofill_handler.cc
@@ -4,8 +4,11 @@
 
 #include "components/autofill/core/browser/autofill_handler.h"
 
+#include "base/bind.h"
 #include "base/containers/adapters.h"
 #include "base/feature_list.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/common/autofill_data_validation.h"
@@ -148,6 +151,8 @@
 
 AutofillHandler::~AutofillHandler() {
   translate_observation_.Reset();
+  if (!query_result_delay_task_.IsCancelled())
+    query_result_delay_task_.Cancel();
 }
 
 void AutofillHandler::OnLanguageDetermined(
@@ -520,6 +525,32 @@
 
   LogAutofillTypePredictionsAvailable(log_manager_, queried_forms);
 
+  // TODO(crbug.com/1176816): Remove the test code after initial integration.
+  int delay = 0;
+  if (auto* cmd = base::CommandLine::ForCurrentProcess()) {
+    // This command line helps to simulate query result arriving after autofill
+    // is triggered and shall be used for manual test only.
+    std::string value = cmd->GetSwitchValueASCII(
+        "autofill-server-query-result-delay-in-seconds");
+    if (!base::StringToInt(value, &delay))
+      delay = 0;
+  }
+
+  if (delay > 0) {
+    query_result_delay_task_.Reset(
+        base::BindOnce(&AutofillHandler::PropagateAutofillPredictionsToDriver,
+                       base::Unretained(this)));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(query_result_delay_task_.callback(), queried_forms),
+        base::TimeDelta::FromSeconds(delay));
+  } else {
+    PropagateAutofillPredictionsToDriver(queried_forms);
+  }
+}
+
+void AutofillHandler::PropagateAutofillPredictionsToDriver(
+    const std::vector<FormStructure*>& queried_forms) {
   // Forward form structures to the password generation manager to detect
   // account creation forms.
   driver()->PropagateAutofillPredictions(queried_forms);
diff --git a/components/autofill/core/browser/autofill_handler.h b/components/autofill/core/browser/autofill_handler.h
index be34dff..5357a35a 100644
--- a/components/autofill/core/browser/autofill_handler.h
+++ b/components/autofill/core/browser/autofill_handler.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/cancelable_callback.h"
 #include "base/compiler_specific.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
@@ -335,6 +336,9 @@
   // |form_structures|.
   void OnFormsParsed(const std::vector<const FormData*>& forms);
 
+  void PropagateAutofillPredictionsToDriver(
+      const std::vector<FormStructure*>& forms);
+
   // Provides driver-level context to the shared code of the component. Must
   // outlive this object.
   AutofillDriver* const driver_;
@@ -369,6 +373,10 @@
   // Tracks whether or not rich query encoding is enabled for this client.
   const bool is_rich_query_enabled_ = false;
 
+  // Task to delay propagate the query result to driver for testing.
+  base::CancelableOnceCallback<void(const std::vector<FormStructure*>&)>
+      query_result_delay_task_;
+
   // Will be not null only for |SaveCardBubbleViewsFullFormBrowserTest|.
   ObserverForTest* observer_for_testing_ = nullptr;
 
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 5413a7b..b018693 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -170,6 +170,8 @@
   // Returns true only if the previewed form should be cleared.
   bool ShouldClearPreviewedForm();
 
+  AutofillOfferManager* offer_manager() { return offer_manager_; }
+
   CreditCardAccessManager* credit_card_access_manager() {
     return credit_card_access_manager_.get();
   }
@@ -675,7 +677,8 @@
   std::unique_ptr<CreditCardAccessManager> credit_card_access_manager_;
 
   // The autofill offer manager, used to to retrieve offers for card
-  // suggestions.
+  // suggestions. Initialized when AutofillManager is created. |offer_manager_|
+  // is never null.
   AutofillOfferManager* offer_manager_;
 
   // Collected information about the autofill form where a credit card will be
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager.cc b/components/autofill/core/browser/payments/autofill_offer_manager.cc
index f469370b..333a4b0 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager.cc
@@ -81,6 +81,37 @@
   }
 }
 
+bool AutofillOfferManager::IsUrlEligible(const GURL& last_committed_url) {
+  GURL last_committed_url_origin = last_committed_url.GetOrigin();
+  return base::ranges::count(eligible_merchant_domains_,
+                             last_committed_url_origin);
+}
+
+std::vector<GURL> AutofillOfferManager::GetEligibleDomainsForOfferForUrl(
+    const GURL& last_committed_url) {
+  std::vector<GURL> linked_domains;
+  std::vector<AutofillOfferData*> offers =
+      personal_data_->GetCreditCardOffers();
+
+  // Check which offer is eligible on current domain, then return the full set
+  // of domains for that offer.
+  for (auto* offer : offers) {
+    if (IsOfferEligible(*offer, last_committed_url.GetOrigin())) {
+      for (auto& domain : offer->merchant_domain) {
+        linked_domains.emplace_back(domain);
+      }
+      break;
+    }
+  }
+
+  // Remove duplicates.
+  base::ranges::sort(linked_domains);
+  linked_domains.erase(base::ranges::unique(linked_domains),
+                       linked_domains.end());
+
+  return linked_domains;
+}
+
 void AutofillOfferManager::UpdateEligibleMerchantDomains() {
   eligible_merchant_domains_.clear();
   std::vector<AutofillOfferData*> offers =
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager.h b/components/autofill/core/browser/payments/autofill_offer_manager.h
index df9fceef..e017873 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager.h
+++ b/components/autofill/core/browser/payments/autofill_offer_manager.h
@@ -42,7 +42,17 @@
   void UpdateSuggestionsWithOffers(const GURL& last_committed_url,
                                    std::vector<Suggestion>& suggestions);
 
+  // Returns true only if the domain of |last_committed_url| has an offer.
+  bool IsUrlEligible(const GURL& last_committed_url);
+
+  // Returns the set of domains linked to a specific offer that contains the
+  // domain of |last_committed_url|.
+  std::vector<GURL> GetEligibleDomainsForOfferForUrl(
+      const GURL& last_committed_url);
+
  private:
+  FRIEND_TEST_ALL_PREFIXES(AutofillOfferManagerTest, IsUrlEligible);
+
   // Queries |personal_data_| to reset the elements of
   // |eligible_merchant_domains_|
   void UpdateEligibleMerchantDomains();
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
index 964cbfa5..b4a8193 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
@@ -71,7 +71,9 @@
 
   void CreateCreditCardOfferForCard(const CreditCard& card,
                                     std::string offer_reward_amount,
-                                    bool expired = false) {
+                                    bool expired = false,
+                                    std::vector<GURL> domains = {
+                                        GURL(kTestUrl)}) {
     AutofillOfferData offer_data;
     offer_data.offer_id = 4444;
     offer_data.offer_reward_amount = offer_reward_amount;
@@ -80,7 +82,7 @@
     } else {
       offer_data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(2);
     }
-    offer_data.merchant_domain = {GURL(kTestUrl)};
+    offer_data.merchant_domain = std::move(domains);
     offer_data.eligible_instrument_id = {card.instrument_id()};
     personal_data_manager_.AddCreditCardOfferData(offer_data);
   }
@@ -194,4 +196,60 @@
   EXPECT_EQ(suggestions[1].backend_id, kTestGuid2);
 }
 
+TEST_F(AutofillOfferManagerTest, IsUrlEligible) {
+  CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100);
+  CreditCard card2 = CreateCreditCard(kTestGuid2, "4111111111111111", 101);
+  CreateCreditCardOfferForCard(
+      card1, "5%", /*expired=*/false,
+      {GURL("http://www.google.com"), GURL("http://www.youtube.com")});
+  CreateCreditCardOfferForCard(card2, "10%", /*expired=*/false,
+                               {GURL("http://maps.google.com")});
+  autofill_offer_manager_->UpdateEligibleMerchantDomains();
+
+  EXPECT_TRUE(
+      autofill_offer_manager_->IsUrlEligible(GURL("http://www.google.com")));
+  EXPECT_FALSE(
+      autofill_offer_manager_->IsUrlEligible(GURL("http://www.example.com")));
+  EXPECT_TRUE(
+      autofill_offer_manager_->IsUrlEligible(GURL("http://maps.google.com")));
+}
+
+TEST_F(AutofillOfferManagerTest,
+       GetEligibleDomainsForOfferForUrl_ReturnNothingWhenFindNoMatch) {
+  CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100);
+  CreateCreditCardOfferForCard(
+      card1, "5%", /*expired=*/false,
+      {GURL("http://www.google.com"), GURL("http://www.youtube.com")});
+
+  EXPECT_EQ(
+      0U, autofill_offer_manager_
+              ->GetEligibleDomainsForOfferForUrl(GURL("http://www.example.com"))
+              .size());
+}
+
+TEST_F(AutofillOfferManagerTest,
+       GetEligibleDomainsForOfferForUrl_ReturnCorrectSetWhenFindMatch) {
+  CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100);
+  CreditCard card2 = CreateCreditCard(kTestGuid2, "4111111111111111", 101);
+  CreateCreditCardOfferForCard(
+      card1, "5%", /*expired=*/false,
+      /*domains=*/
+      {GURL("http://www.google.com"), GURL("http://www.youtube.com")});
+  CreateCreditCardOfferForCard(
+      card2, "5%", /*expired=*/false,
+      /*domains=*/
+      {GURL("http://www.example.com"), GURL("http://www.example2.com")});
+
+  std::vector<GURL> eligible_domain =
+      autofill_offer_manager_->GetEligibleDomainsForOfferForUrl(
+          GURL("http://www.example.com"));
+  EXPECT_EQ(2U, eligible_domain.size());
+  EXPECT_NE(eligible_domain.end(),
+            std::find(eligible_domain.begin(), eligible_domain.end(),
+                      GURL("http://www.example.com")));
+  EXPECT_NE(eligible_domain.end(),
+            std::find(eligible_domain.begin(), eligible_domain.end(),
+                      GURL("http://www.example2.com")));
+}
+
 }  // namespace autofill
diff --git a/components/cronet/OWNERS b/components/cronet/OWNERS
index fb59c70..15c012eb 100644
--- a/components/cronet/OWNERS
+++ b/components/cronet/OWNERS
@@ -1 +1,2 @@
+torne@chromium.org
 file://net/OWNERS
diff --git a/components/cronet/android/cronet_integrated_mode_state.cc b/components/cronet/android/cronet_integrated_mode_state.cc
index 6f5e1c2..9b68873 100644
--- a/components/cronet/android/cronet_integrated_mode_state.cc
+++ b/components/cronet/android/cronet_integrated_mode_state.cc
@@ -15,7 +15,7 @@
 
 void SetIntegratedModeNetworkTaskRunner(
     base::SingleThreadTaskRunner* network_task_runner) {
-  CHECK_EQ(base::subtle::Acquire_CompareAndSwap(
+  CHECK_EQ(base::subtle::Release_CompareAndSwap(
                &g_integrated_mode_network_task_runner, 0,
                reinterpret_cast<base::subtle::AtomicWord>(network_task_runner)),
            0);
@@ -23,7 +23,7 @@
 
 base::SingleThreadTaskRunner* GetIntegratedModeNetworkTaskRunner() {
   base::subtle::AtomicWord task_runner =
-      base::subtle::Release_Load(&g_integrated_mode_network_task_runner);
+      base::subtle::Acquire_Load(&g_integrated_mode_network_task_runner);
   CHECK(task_runner);
   return reinterpret_cast<base::SingleThreadTaskRunner*>(task_runner);
 }
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index b2457f1..e1d930fa 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -326,7 +326,7 @@
     bool can_minimize,
     int container,
     bool default_scale_cancellation)
-    : ShellSurfaceBase(surface, gfx::Point(), true, can_minimize, container),
+    : ShellSurfaceBase(surface, gfx::Point(), can_minimize, container),
       current_pin_(chromeos::WindowPinType::kNone),
       use_default_scale_cancellation_(default_scale_cancellation) {
   server_side_resize_ = true;
diff --git a/components/exo/display.cc b/components/exo/display.cc
index df36a295..222da6fca 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -148,7 +148,7 @@
   }
 
   return std::make_unique<ShellSurface>(
-      surface, gfx::Point(), true /* activatable */, false /* can_minimize */,
+      surface, gfx::Point(), /*can_minimize=*/false,
       ash::desks_util::GetActiveDeskContainerId());
 }
 
@@ -162,7 +162,7 @@
   }
 
   return std::make_unique<XdgShellSurface>(
-      surface, gfx::Point(), true /* activatable */, false /* can_minimize */,
+      surface, gfx::Point(), /*can_minimize=*/false,
       ash::desks_util::GetActiveDeskContainerId());
 }
 
diff --git a/components/exo/pointer_unittest.cc b/components/exo/pointer_unittest.cc
index daa478e..1820754 100644
--- a/components/exo/pointer_unittest.cc
+++ b/components/exo/pointer_unittest.cc
@@ -547,9 +547,9 @@
                         gfx::Vector2d(1, 1));
 
   std::unique_ptr<Surface> child_surface(new Surface);
-  std::unique_ptr<ShellSurface> child_shell_surface(
-      new ShellSurface(child_surface.get(), gfx::Point(9, 9), true, false,
-                       ash::desks_util::GetActiveDeskContainerId()));
+  std::unique_ptr<ShellSurface> child_shell_surface(new ShellSurface(
+      child_surface.get(), gfx::Point(9, 9), /*can_minimize=*/false,
+      ash::desks_util::GetActiveDeskContainerId()));
   child_shell_surface->DisableMovement();
   child_shell_surface->SetParent(shell_surface.get());
   gfx::Size child_buffer_size(15, 15);
@@ -709,7 +709,7 @@
   // Create modal surface.
   std::unique_ptr<Surface> surface(new Surface);
   std::unique_ptr<ShellSurface> shell_surface(
-      new ShellSurface(surface.get(), gfx::Point(), true, false,
+      new ShellSurface(surface.get(), gfx::Point(), /*can_minimize=*/false,
                        ash::kShellWindowId_SystemModalContainer));
   shell_surface->DisableMovement();
   std::unique_ptr<Buffer> buffer(
@@ -768,7 +768,7 @@
   // Create surface for modal window.
   std::unique_ptr<Surface> surface2(new Surface);
   std::unique_ptr<ShellSurface> shell_surface2(
-      new ShellSurface(surface2.get(), gfx::Point(), true, false,
+      new ShellSurface(surface2.get(), gfx::Point(), /*can_minimize=*/false,
                        ash::kShellWindowId_SystemModalContainer));
   shell_surface2->DisableMovement();
   std::unique_ptr<Buffer> buffer2(
@@ -830,7 +830,7 @@
   // Create modal surface.
   std::unique_ptr<Surface> surface(new Surface);
   std::unique_ptr<ShellSurface> shell_surface(
-      new ShellSurface(surface.get(), gfx::Point(), true, false,
+      new ShellSurface(surface.get(), gfx::Point(), /*can_minimize=*/false,
                        ash::kShellWindowId_SystemModalContainer));
   shell_surface->DisableMovement();
   std::unique_ptr<Buffer> buffer(
@@ -881,7 +881,7 @@
   // Create another surface for a non-modal window.
   std::unique_ptr<Surface> surface2(new Surface);
   std::unique_ptr<ShellSurface> shell_surface2(
-      new ShellSurface(surface2.get(), gfx::Point(), true, false,
+      new ShellSurface(surface2.get(), gfx::Point(), /*can_minimize=*/false,
                        ash::kShellWindowId_SystemModalContainer));
   shell_surface2->DisableMovement();
   std::unique_ptr<Buffer> buffer2(
@@ -1043,7 +1043,7 @@
       gfx::Vector2d(10, 10);
   auto child_surface = std::make_unique<Surface>();
   auto child_shell_surface = std::make_unique<ShellSurface>(
-      child_surface.get(), child_surface_origin, true, false,
+      child_surface.get(), child_surface_origin, /*can_minimize=*/false,
       ash::desks_util::GetActiveDeskContainerId());
   child_shell_surface->DisableMovement();
   child_shell_surface->SetParent(shell_surface.get());
@@ -1171,7 +1171,7 @@
 
   auto child_surface = std::make_unique<Surface>();
   auto child_shell_surface = std::make_unique<ShellSurface>(
-      child_surface.get(), gfx::Point(), true, false,
+      child_surface.get(), gfx::Point(), /*can_minimize=*/false,
       ash::desks_util::GetActiveDeskContainerId());
   child_shell_surface->DisableMovement();
   child_shell_surface->SetParent(shell_surface.get());
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index 4031bdee..9ddb1b0 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -92,16 +92,14 @@
 
 ShellSurface::ShellSurface(Surface* surface,
                            const gfx::Point& origin,
-                           bool activatable,
                            bool can_minimize,
                            int container)
-    : ShellSurfaceBase(surface, origin, activatable, can_minimize, container) {}
+    : ShellSurfaceBase(surface, origin, can_minimize, container) {}
 
 ShellSurface::ShellSurface(Surface* surface)
     : ShellSurfaceBase(surface,
                        gfx::Point(),
-                       true,
-                       true,
+                       /*can_minimize=*/true,
                        ash::desks_util::GetActiveDeskContainerId()) {}
 
 ShellSurface::~ShellSurface() {
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index 31f12a4..45ee33b 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -31,7 +31,6 @@
   // specified as part of the geometry is relative to the shell surface.
   ShellSurface(Surface* surface,
                const gfx::Point& origin,
-               bool activatable,
                bool can_minimize,
                int container);
   explicit ShellSurface(Surface* surface);
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 0a81339c..6388012 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -298,13 +298,11 @@
 
 ShellSurfaceBase::ShellSurfaceBase(Surface* surface,
                                    const gfx::Point& origin,
-                                   bool activatable,
                                    bool can_minimize,
                                    int container)
     : SurfaceTreeHost(base::StringPrintf("ExoShellSurfaceHost-%d", shell_id)),
       origin_(origin),
       container_(container),
-      activatable_(activatable),
       can_minimize_(can_minimize) {
   WMHelper::GetInstance()->AddActivationObserver(this);
   surface->AddSurfaceObserver(this);
diff --git a/components/exo/shell_surface_base.h b/components/exo/shell_surface_base.h
index 599dc82..a0a84e01 100644
--- a/components/exo/shell_surface_base.h
+++ b/components/exo/shell_surface_base.h
@@ -58,7 +58,6 @@
   // specified as part of the geometry is relative to the shell surface.
   ShellSurfaceBase(Surface* surface,
                    const gfx::Point& origin,
-                   bool activatable,
                    bool can_minimize,
                    int container);
   ~ShellSurfaceBase() override;
diff --git a/components/exo/test/exo_test_helper.cc b/components/exo/test/exo_test_helper.cc
index d204e2b..74b1f66 100644
--- a/components/exo/test/exo_test_helper.cc
+++ b/components/exo/test/exo_test_helper.cc
@@ -110,8 +110,9 @@
   surface_.reset(new Surface());
   int container = is_modal ? ash::kShellWindowId_SystemModalContainer
                            : ash::desks_util::GetActiveDeskContainerId();
-  shell_surface_ = std::make_unique<ShellSurface>(surface_.get(), gfx::Point(),
-                                                  true, false, container);
+  shell_surface_ =
+      std::make_unique<ShellSurface>(surface_.get(), gfx::Point(),
+                                     /*can minimize=*/false, container);
 
   buffer_.reset(new Buffer(std::move(gpu_buffer)));
   surface_->Attach(buffer_.get());
diff --git a/components/exo/touch_unittest.cc b/components/exo/touch_unittest.cc
index ccfacc5..bc348ee 100644
--- a/components/exo/touch_unittest.cc
+++ b/components/exo/touch_unittest.cc
@@ -548,7 +548,7 @@
 
   auto child_surface = std::make_unique<Surface>();
   auto child_shell_surface = std::make_unique<ShellSurface>(
-      child_surface.get(), gfx::Point(), true, false,
+      child_surface.get(), gfx::Point(), /*can_minimize=*/false,
       ash::desks_util::GetActiveDeskContainerId());
   child_shell_surface->DisableMovement();
   child_shell_surface->SetParent(shell_surface.get());
diff --git a/components/exo/ui_lock_controller_unittest.cc b/components/exo/ui_lock_controller_unittest.cc
index f57d70a..7c1f115 100644
--- a/components/exo/ui_lock_controller_unittest.cc
+++ b/components/exo/ui_lock_controller_unittest.cc
@@ -61,7 +61,6 @@
     auto surface = std::make_unique<Surface>();
     auto shell_surface = std::make_unique<ShellSurface>(
         surface.get(), gfx::Point{0, 0},
-        /*activatable=*/true,
         /*can_minimize=*/true, ash::desks_util::GetActiveDeskContainerId());
     auto buffer = std::make_unique<Buffer>(
         exo_test_helper()->CreateGpuMemoryBuffer({w, h}));
diff --git a/components/exo/xdg_shell_surface.cc b/components/exo/xdg_shell_surface.cc
index b6351cff..15d09c4 100644
--- a/components/exo/xdg_shell_surface.cc
+++ b/components/exo/xdg_shell_surface.cc
@@ -19,10 +19,9 @@
 
 XdgShellSurface::XdgShellSurface(Surface* surface,
                                  const gfx::Point& origin,
-                                 bool activatable,
                                  bool can_minimize,
                                  int container)
-    : ShellSurface(surface, origin, activatable, can_minimize, container) {}
+    : ShellSurface(surface, origin, can_minimize, container) {}
 
 XdgShellSurface::~XdgShellSurface() {}
 
diff --git a/components/exo/xdg_shell_surface.h b/components/exo/xdg_shell_surface.h
index 87973a9..a84ca77 100644
--- a/components/exo/xdg_shell_surface.h
+++ b/components/exo/xdg_shell_surface.h
@@ -44,11 +44,11 @@
   // specified as part of the geometry is relative to the shell surface.
   XdgShellSurface(Surface* surface,
                   const gfx::Point& origin,
-                  bool activatable,
                   bool can_minimize,
                   int container);
   ~XdgShellSurface() override;
 
+  // ShellSurfaceBase::
   void OverrideInitParams(views::Widget::InitParams* params) override;
 
   bool x_flipped() const { return x_flipped_; }
diff --git a/components/exo/xdg_shell_surface_unittest.cc b/components/exo/xdg_shell_surface_unittest.cc
index a1ff680c..33e4c64 100644
--- a/components/exo/xdg_shell_surface_unittest.cc
+++ b/components/exo/xdg_shell_surface_unittest.cc
@@ -27,7 +27,6 @@
     auto surface = std::make_unique<Surface>();
     auto shell_surface = std::make_unique<XdgShellSurface>(
         surface.get(), gfx::Point{0, 0},
-        /*activatable=*/true,
         /*can_minimize=*/true, ash::desks_util::GetActiveDeskContainerId());
     auto buffer = std::make_unique<Buffer>(
         exo_test_helper()->CreateGpuMemoryBuffer({w, h}));
diff --git a/components/feed/core/proto/v2/store.proto b/components/feed/core/proto/v2/store.proto
index 206d9059..68224893 100644
--- a/components/feed/core/proto/v2/store.proto
+++ b/components/feed/core/proto/v2/store.proto
@@ -16,6 +16,7 @@
 // This data is sourced from the wire protocol, which is converted upon receipt.
 // This would replace both Journal and Content stores.
 //
+// TODO(crbug/1152592): Support multiple streams.
 // This is the 'value' in the key/value store.
 // Keys are defined as:
 // S/<stream-id>                    -> stream_data
diff --git a/components/feed/core/v2/config.cc b/components/feed/core/v2/config.cc
index 4ff2188..8f433fb 100644
--- a/components/feed/core/v2/config.cc
+++ b/components/feed/core/v2/config.cc
@@ -34,6 +34,11 @@
           kInterestFeedV2, "max_feed_query_requests_per_day",
           config->max_feed_query_requests_per_day);
 
+  config->max_next_page_requests_per_day =
+      base::GetFieldTrialParamByFeatureAsInt(
+          kInterestFeedV2, "max_next_page_requests_per_day",
+          config->max_next_page_requests_per_day);
+
   config->max_action_upload_requests_per_day =
       base::GetFieldTrialParamByFeatureAsInt(
           kInterestFeedV2, "max_action_upload_requests_per_day",
diff --git a/components/feed/core/v2/config.h b/components/feed/core/v2/config.h
index 1f375149..19b3362 100644
--- a/components/feed/core/v2/config.h
+++ b/components/feed/core/v2/config.h
@@ -14,8 +14,10 @@
 // The Feed configuration. Default values appear below. Always use
 // |GetFeedConfig()| to get the current configuration.
 struct Config {
-  // Maximum number of FeedQuery or action upload requests per day.
+  // Maximum number of requests per day for FeedQuery, NextPage, and
+  // ActionUpload.
   int max_feed_query_requests_per_day = 20;
+  int max_next_page_requests_per_day = 20;
   int max_action_upload_requests_per_day = 20;
   // We'll always attempt to refresh content older than this.
   base::TimeDelta stale_content_threshold = base::TimeDelta::FromHours(4);
diff --git a/components/feed/core/v2/enums.h b/components/feed/core/v2/enums.h
index 596e78a00..eff9e25 100644
--- a/components/feed/core/v2/enums.h
+++ b/components/feed/core/v2/enums.h
@@ -12,6 +12,7 @@
 enum class NetworkRequestType : int {
   kFeedQuery = 0,
   kUploadActions = 1,
+  kNextPage = 2,
 };
 
 // This must be kept in sync with FeedLoadStreamStatus in enums.xml.
diff --git a/components/feed/core/v2/feed_network.h b/components/feed/core/v2/feed_network.h
index a5676de45..182b16ca 100644
--- a/components/feed/core/v2/feed_network.h
+++ b/components/feed/core/v2/feed_network.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/callback.h"
+#include "components/feed/core/v2/enums.h"
 #include "components/feed/core/v2/public/types.h"
 
 namespace feedwire {
@@ -51,6 +52,7 @@
   // |callback| will be called unless the request is canceled with
   // |CancelRequests()|.
   virtual void SendQueryRequest(
+      NetworkRequestType request_type,
       const feedwire::Request& request,
       bool force_signed_out_request,
       base::OnceCallback<void(QueryRequestResult)> callback) = 0;
diff --git a/components/feed/core/v2/feed_network_impl.cc b/components/feed/core/v2/feed_network_impl.cc
index 43f82af4..93d0f11 100644
--- a/components/feed/core/v2/feed_network_impl.cc
+++ b/components/feed/core/v2/feed_network_impl.cc
@@ -93,10 +93,11 @@
 namespace {
 
 void ParseAndForwardQueryResponse(
+    NetworkRequestType request_type,
     base::OnceCallback<void(FeedNetwork::QueryRequestResult)> result_callback,
     RawResponse raw_response) {
   MetricsReporter::NetworkRequestComplete(
-      NetworkRequestType::kFeedQuery, raw_response.response_info.status_code);
+      request_type, raw_response.response_info.status_code);
   FeedNetwork::QueryRequestResult result;
   result.response_info = raw_response.response_info;
   if (result.response_info.status_code == 200) {
@@ -430,6 +431,7 @@
 FeedNetworkImpl::~FeedNetworkImpl() = default;
 
 void FeedNetworkImpl::SendQueryRequest(
+    NetworkRequestType request_type,
     const feedwire::Request& request,
     bool force_signed_out_request,
     base::OnceCallback<void(QueryRequestResult)> callback) {
@@ -469,7 +471,8 @@
                                   url);
   Send(url, "GET", /*request_body=*/{}, force_signed_out_request,
        /*allow_bless_auth=*/host_overridden,
-       base::BindOnce(&ParseAndForwardQueryResponse, std::move(callback)));
+       base::BindOnce(&ParseAndForwardQueryResponse, request_type,
+                      std::move(callback)));
 }
 
 void FeedNetworkImpl::SendActionRequest(
diff --git a/components/feed/core/v2/feed_network_impl.h b/components/feed/core/v2/feed_network_impl.h
index e9e88aa..221325e 100644
--- a/components/feed/core/v2/feed_network_impl.h
+++ b/components/feed/core/v2/feed_network_impl.h
@@ -48,6 +48,7 @@
   // FeedNetwork.
 
   void SendQueryRequest(
+      NetworkRequestType request_type,
       const feedwire::Request& request,
       bool force_signed_out_request,
       base::OnceCallback<void(QueryRequestResult)> callback) override;
diff --git a/components/feed/core/v2/feed_network_impl_unittest.cc b/components/feed/core/v2/feed_network_impl_unittest.cc
index 3f8c6a0..149bd85 100644
--- a/components/feed/core/v2/feed_network_impl_unittest.cc
+++ b/components/feed/core/v2/feed_network_impl_unittest.cc
@@ -207,7 +207,8 @@
 
 TEST_F(FeedNetworkTest, SendQueryRequestEmpty) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(feedwire::Request(), false, receiver.Bind());
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   feedwire::Request(), false, receiver.Bind());
 
   ASSERT_TRUE(receiver.GetResult());
   const QueryRequestResult& result = *receiver.GetResult();
@@ -217,7 +218,8 @@
 
 TEST_F(FeedNetworkTest, SendQueryRequestSendsValidRequest) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   network::ResourceRequest resource_request =
       RespondToQueryRequest("", net::HTTP_OK);
@@ -239,7 +241,8 @@
 TEST_F(FeedNetworkTest, SendQueryRequestForceSignedOut) {
   CallbackReceiver<QueryRequestResult> receiver;
   feed_network()->SendQueryRequest(
-      GetTestFeedRequest(), /*force_signed_out_request=*/true, receiver.Bind());
+      NetworkRequestType::kFeedQuery, GetTestFeedRequest(),
+      /*force_signed_out_request=*/true, receiver.Bind());
   network::ResourceRequest resource_request =
       RespondToQueryRequest("", net::HTTP_OK);
 
@@ -252,7 +255,8 @@
 
 TEST_F(FeedNetworkTest, SendQueryRequestInvalidResponse) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   RespondToQueryRequest("invalid", net::HTTP_OK);
 
@@ -264,7 +268,8 @@
 
 TEST_F(FeedNetworkTest, SendQueryRequestReceivesResponse) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK);
 
@@ -282,7 +287,8 @@
 
 TEST_F(FeedNetworkTest, SendQueryRequestIgnoresBodyForNon200Response) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_FORBIDDEN);
 
@@ -297,7 +303,8 @@
 
 TEST_F(FeedNetworkTest, CancelRequest) {
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   feed_network()->CancelRequests();
   task_environment_.FastForwardUntilNoTasksRemain();
@@ -308,7 +315,8 @@
 TEST_F(FeedNetworkTest, RequestTimeout) {
   base::HistogramTester histogram_tester;
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   task_environment_.FastForwardBy(TimeDelta::FromSeconds(30));
 
@@ -322,11 +330,13 @@
 
 TEST_F(FeedNetworkTest, ParallelRequests) {
   CallbackReceiver<QueryRequestResult> receiver1, receiver2;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver1.Bind());
   // Make another request with a different URL so Respond() won't affect both
   // requests.
   feed_network()->SendQueryRequest(
+      NetworkRequestType::kFeedQuery,
       GetTestFeedRequest(feedwire::FeedQuery::NEXT_PAGE_SCROLL), false,
       receiver2.Bind());
 
@@ -350,7 +360,8 @@
 TEST_F(FeedNetworkTest, ShouldReportResponseStatusCode) {
   CallbackReceiver<QueryRequestResult> receiver;
   base::HistogramTester histogram_tester;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_FORBIDDEN);
 
@@ -365,7 +376,8 @@
   CallbackReceiver<QueryRequestResult> receiver;
   base::HistogramTester histogram_tester;
 
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
       GoogleServiceAuthError(
@@ -391,7 +403,8 @@
 TEST_F(FeedNetworkTest, ShouldIncludeAPIKeyForNoSignedInUser) {
   identity_env()->ClearPrimaryAccount();
   CallbackReceiver<QueryRequestResult> receiver;
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
 
   network::ResourceRequest resource_request =
@@ -407,7 +420,8 @@
   CallbackReceiver<QueryRequestResult> receiver;
   const TimeDelta kDuration = TimeDelta::FromMilliseconds(12345);
 
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
   task_environment_.FastForwardBy(kDuration);
   RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK);
@@ -423,7 +437,8 @@
   CallbackReceiver<QueryRequestResult> receiver;
   profile_prefs().SetString(feed::prefs::kHostOverrideHost,
                             "http://www.newhost.com/");
-  feed_network()->SendQueryRequest(GetTestFeedRequest(), false,
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), false,
                                    receiver.Bind());
 
   ASSERT_EQ("www.newhost.com", GetPendingRequestURL().host());
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index ec4a14ac..52474d5 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -163,6 +163,9 @@
   return LocalActionId(id);
 }
 
+FeedStream::Stream::Stream() : type(kInterestStream) {}
+FeedStream::Stream::~Stream() = default;
+
 FeedStream::FeedStream(RefreshTaskScheduler* refresh_task_scheduler,
                        MetricsReporter* metrics_reporter,
                        Delegate* delegate,
@@ -191,9 +194,9 @@
   static WireResponseTranslator default_translator;
   wire_response_translator_ = &default_translator;
 
-  surface_updater_ = std::make_unique<SurfaceUpdater>(metrics_reporter_);
-  offline_page_spy_ = std::make_unique<OfflinePageSpy>(surface_updater_.get(),
-                                                       offline_page_model);
+  Stream& stream = GetStream(kInterestStream);
+  offline_page_spy_ = std::make_unique<OfflinePageSpy>(
+      stream.surface_updater.get(), offline_page_model);
 
   if (prefetch_service_) {
     offline_suggestions_provider_ =
@@ -218,8 +221,36 @@
 
 FeedStream::~FeedStream() = default;
 
+FeedStream::Stream* FeedStream::FindStream(const StreamType& stream_type) {
+  auto iter = streams_.find(stream_type);
+  return (iter != streams_.end()) ? &iter->second : nullptr;
+}
+
+const FeedStream::Stream* FeedStream::FindStream(
+    const StreamType& stream_type) const {
+  return const_cast<FeedStream*>(this)->FindStream(stream_type);
+}
+
+FeedStream::Stream& FeedStream::GetStream(const StreamType& stream_type) {
+  auto iter = streams_.find(stream_type);
+  if (iter != streams_.end())
+    return iter->second;
+  FeedStream::Stream& new_stream = streams_[stream_type];
+  new_stream.type = stream_type;
+  new_stream.surface_updater =
+      std::make_unique<SurfaceUpdater>(metrics_reporter_);
+  return new_stream;
+}
+
+StreamModel* FeedStream::GetModel(const StreamType& stream_type) {
+  Stream* stream = FindStream(stream_type);
+  return stream ? stream->model.get() : nullptr;
+}
+
 void FeedStream::TriggerStreamLoad() {
-  if (model_ || model_loading_in_progress_)
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
+  if (stream.model || stream.model_loading_in_progress)
     return;
 
   // If we should not load the stream, abort and send a zero-state update.
@@ -229,8 +260,9 @@
     return;
   }
 
-  model_loading_in_progress_ = true;
-  surface_updater_->LoadStreamStarted();
+  stream.model_loading_in_progress = true;
+
+  stream.surface_updater->LoadStreamStarted();
   task_queue_.AddTask(std::make_unique<LoadStreamTask>(
       LoadStreamTask::LoadType::kInitialLoad, this,
       base::BindOnce(&FeedStream::InitialStreamLoadComplete,
@@ -238,6 +270,8 @@
 }
 
 void FeedStream::InitialStreamLoadComplete(LoadStreamTask::Result result) {
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
   PopulateDebugStreamData(result, *profile_prefs_);
   metrics_reporter_->OnLoadStream(
       result.load_from_store_status, result.final_status,
@@ -245,9 +279,9 @@
       std::move(result.latencies));
   UpdateIsActivityLoggingEnabled();
 
-  model_loading_in_progress_ = false;
-
-  surface_updater_->LoadStreamComplete(model_ != nullptr, result.final_status);
+  stream.model_loading_in_progress = false;
+  stream.surface_updater->LoadStreamComplete(stream.model != nullptr,
+                                             result.final_status);
 
   if (result.loaded_new_content_from_network && prefetch_service_)
     prefetch_service_->NewSuggestionsAvailable();
@@ -268,10 +302,12 @@
 }
 
 void FeedStream::UpdateIsActivityLoggingEnabled() {
+  Stream& stream = GetStream(kInterestStream);
   is_activity_logging_enabled_ =
-      model_ &&
-      ((model_->signed_in() && model_->logging_enabled()) ||
-       (!model_->signed_in() && GetFeedConfig().send_signed_out_session_logs));
+      stream.model &&
+      ((stream.model->signed_in() && stream.model->logging_enabled()) ||
+       (!stream.model->signed_in() &&
+        GetFeedConfig().send_signed_out_session_logs));
 }
 
 std::string FeedStream::GetSessionId() const {
@@ -284,54 +320,63 @@
 
 void FeedStream::AttachSurface(SurfaceInterface* surface) {
   metrics_reporter_->SurfaceOpened(surface->GetSurfaceId());
-
+  Stream& stream = GetStream(surface->GetStreamType());
   // Skip normal processing when overriding stream data from the internals page.
   if (forced_stream_update_for_debugging_.updated_slices_size() > 0) {
-    surface_updater_->SurfaceAdded(surface);
+    stream.surface_updater->SurfaceAdded(surface);
     surface->StreamUpdate(forced_stream_update_for_debugging_);
     return;
   }
 
   TriggerStreamLoad();
-  surface_updater_->SurfaceAdded(surface);
+  stream.surface_updater->SurfaceAdded(surface);
 
   // Cancel any scheduled model unload task.
-  ++unload_on_detach_sequence_number_;
+  ++stream.unload_on_detach_sequence_number;
   UpdateCanUploadActionsWithNoticeCard();
 }
 
 void FeedStream::DetachSurface(SurfaceInterface* surface) {
+  Stream& stream = GetStream(surface->GetStreamType());
   metrics_reporter_->SurfaceClosed(surface->GetSurfaceId());
-  surface_updater_->SurfaceRemoved(surface);
+  stream.surface_updater->SurfaceRemoved(surface);
   UpdateCanUploadActionsWithNoticeCard();
-  ScheduleModelUnloadIfNoSurfacesAttached();
+  ScheduleModelUnloadIfNoSurfacesAttached(surface->GetStreamType());
 }
 
-void FeedStream::ScheduleModelUnloadIfNoSurfacesAttached() {
-  if (surface_updater_->HasSurfaceAttached())
+void FeedStream::ScheduleModelUnloadIfNoSurfacesAttached(
+    const StreamType& stream_type) {
+  Stream& stream = GetStream(stream_type);
+  if (stream.surface_updater->HasSurfaceAttached())
     return;
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&FeedStream::AddUnloadModelIfNoSurfacesAttachedTask,
-                     GetWeakPtr(), unload_on_detach_sequence_number_),
+                     GetWeakPtr(), stream.type,
+                     stream.unload_on_detach_sequence_number),
       GetFeedConfig().model_unload_timeout);
 }
 
-void FeedStream::AddUnloadModelIfNoSurfacesAttachedTask(int sequence_number) {
+void FeedStream::AddUnloadModelIfNoSurfacesAttachedTask(
+    const StreamType& stream_type,
+    int sequence_number) {
+  Stream& stream = GetStream(stream_type);
   // Don't continue if unload_on_detach_sequence_number_ has changed.
-  if (unload_on_detach_sequence_number_ != sequence_number)
+  if (stream.unload_on_detach_sequence_number != sequence_number)
     return;
 
   task_queue_.AddTask(std::make_unique<offline_pages::ClosureTask>(
       base::BindOnce(&FeedStream::UnloadModelIfNoSurfacesAttachedTask,
-                     base::Unretained(this))));
+                     base::Unretained(this), stream_type)));
 }
 
-void FeedStream::UnloadModelIfNoSurfacesAttachedTask() {
-  if (surface_updater_->HasSurfaceAttached())
+void FeedStream::UnloadModelIfNoSurfacesAttachedTask(
+    const StreamType& stream_type) {
+  Stream& stream = GetStream(stream_type);
+  if (stream.surface_updater->HasSurfaceAttached())
     return;
-  UnloadModel();
+  UnloadModel(stream_type);
 }
 
 bool FeedStream::IsArticlesListVisible() {
@@ -346,9 +391,10 @@
   return profile_prefs_->GetBoolean(prefs::kEnableSnippets);
 }
 
-void FeedStream::LoadMore(SurfaceId surface_id,
+void FeedStream::LoadMore(const SurfaceInterface& surface,
                           base::OnceCallback<void(bool)> callback) {
-  if (!model_) {
+  Stream& stream = GetStream(surface.GetStreamType());
+  if (!stream.model) {
     DLOG(ERROR) << "Ignoring LoadMore() before the model is loaded";
     return std::move(callback).Run(false);
   }
@@ -360,8 +406,8 @@
     return std::move(callback).Run(false);
   }
 
-  metrics_reporter_->OnLoadMoreBegin(surface_id);
-  surface_updater_->SetLoadingMore(true);
+  metrics_reporter_->OnLoadMoreBegin(surface.GetSurfaceId());
+  stream.surface_updater->SetLoadingMore(true);
 
   // Have at most one in-flight LoadMore() request. Send the result to all
   // requestors.
@@ -375,8 +421,10 @@
 
 void FeedStream::LoadMoreComplete(LoadMoreTask::Result result) {
   UpdateIsActivityLoggingEnabled();
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
   metrics_reporter_->OnLoadMore(result.final_status);
-  surface_updater_->SetLoadingMore(false);
+  stream.surface_updater->SetLoadingMore(false);
   std::vector<base::OnceCallback<void(bool)>> moved_callbacks =
       std::move(load_more_complete_callbacks_);
   bool success = result.final_status == LoadStreamStatus::kLoadedFromNetwork;
@@ -389,45 +437,55 @@
 }
 
 void FeedStream::ExecuteOperations(
+    const StreamType& stream_type,
     std::vector<feedstore::DataOperation> operations) {
-  if (!model_) {
+  StreamModel* model = GetModel(stream_type);
+  if (!model) {
     DLOG(ERROR) << "Calling ExecuteOperations before the model is loaded";
     return;
   }
-  return model_->ExecuteOperations(std::move(operations));
+  return model->ExecuteOperations(std::move(operations));
 }
 
 EphemeralChangeId FeedStream::CreateEphemeralChange(
+    const StreamType& stream_type,
     std::vector<feedstore::DataOperation> operations) {
-  if (!model_) {
+  StreamModel* model = GetModel(stream_type);
+  if (!model) {
     DLOG(ERROR) << "Calling CreateEphemeralChange before the model is loaded";
     return {};
   }
   metrics_reporter_->OtherUserAction(FeedUserActionType::kEphemeralChange);
-  return model_->CreateEphemeralChange(std::move(operations));
+  return model->CreateEphemeralChange(std::move(operations));
 }
 
 EphemeralChangeId FeedStream::CreateEphemeralChangeFromPackedData(
+    const StreamType& stream_type,
     base::StringPiece data) {
   feedpacking::DismissData msg;
   msg.ParseFromArray(data.data(), data.size());
-  return CreateEphemeralChange(TranslateDismissData(base::Time::Now(), msg));
+  return CreateEphemeralChange(stream_type,
+                               TranslateDismissData(base::Time::Now(), msg));
 }
 
-bool FeedStream::CommitEphemeralChange(EphemeralChangeId id) {
-  if (!model_)
+bool FeedStream::CommitEphemeralChange(const StreamType& stream_type,
+                                       EphemeralChangeId id) {
+  StreamModel* model = GetModel(stream_type);
+  if (!model)
     return false;
   metrics_reporter_->OtherUserAction(
       FeedUserActionType::kEphemeralChangeCommited);
-  return model_->CommitEphemeralChange(id);
+  return model->CommitEphemeralChange(id);
 }
 
-bool FeedStream::RejectEphemeralChange(EphemeralChangeId id) {
-  if (!model_)
+bool FeedStream::RejectEphemeralChange(const StreamType& stream_type,
+                                       EphemeralChangeId id) {
+  StreamModel* model = GetModel(stream_type);
+  if (!model)
     return false;
   metrics_reporter_->OtherUserAction(
       FeedUserActionType::kEphemeralChangeRejected);
-  return model_->RejectEphemeralChange(id);
+  return model->RejectEphemeralChange(id);
 }
 
 void FeedStream::ProcessThereAndBackAgain(base::StringPiece data) {
@@ -478,18 +536,21 @@
 }
 
 void FeedStream::ForceRefreshForDebuggingTask() {
-  UnloadModel();
+  UnloadModel(kInterestStream);
   store_->ClearStreamData(base::DoNothing());
   TriggerStreamLoad();
 }
 
 std::string FeedStream::DumpStateForDebugging() {
+  Stream& stream = GetStream(kInterestStream);
   std::stringstream ss;
-  if (model_) {
-    ss << "model loaded, " << model_->GetContentList().size() << " contents, "
-       << "signed_in=" << model_->signed_in()
-       << ", logging_enabled=" << model_->logging_enabled()
-       << ", privacy_notice_fulfilled=" << model_->privacy_notice_fulfilled();
+  if (stream.model) {
+    ss << "model loaded, " << stream.model->GetContentList().size()
+       << " contents, "
+       << "signed_in=" << stream.model->signed_in()
+       << ", logging_enabled=" << stream.model->logging_enabled()
+       << ", privacy_notice_fulfilled="
+       << stream.model->privacy_notice_fulfilled();
   }
   RequestSchedule schedule = prefs::GetRequestSchedule(*profile_prefs_);
   if (schedule.refresh_offsets.empty()) {
@@ -519,7 +580,9 @@
 }
 
 bool FeedStream::HasSurfaceAttached() const {
-  return surface_updater_->HasSurfaceAttached();
+  // TODO(crbug/1152592): Make ClearAll() work with multiple streams.
+  const Stream* stream = FindStream(kInterestStream);
+  return stream && stream->surface_updater->HasSurfaceAttached();
 }
 
 void FeedStream::LoadModelForTesting(std::unique_ptr<StreamModel> model) {
@@ -562,7 +625,9 @@
   // being loaded. Because |ShouldAttemptLoad()| is used both before and during
   // the load process, we need to ignore this check when |model_loading| is
   // true.
-  if (model_ || (!model_loading && model_loading_in_progress_))
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
+  if (stream.model || (!model_loading && stream.model_loading_in_progress))
     return LoadStreamStatus::kModelAlreadyLoaded;
 
   if (!IsArticlesListVisible())
@@ -588,6 +653,8 @@
 
 LoadStreamStatus FeedStream::ShouldMakeFeedQueryRequest(bool is_load_more,
                                                         bool consume_quota) {
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
   if (!is_load_more) {
     // Time has passed since calling |ShouldAttemptLoad()|, call it again to
     // confirm we should still attempt loading.
@@ -598,7 +665,7 @@
     }
   } else {
     // LoadMore requires a next page token.
-    if (!model_ || model_->GetNextPageToken().empty()) {
+    if (!stream.model || stream.model->GetNextPageToken().empty()) {
       return LoadStreamStatus::kCannotLoadMoreNoNextPageToken;
     }
   }
@@ -607,8 +674,9 @@
     return LoadStreamStatus::kCannotLoadFromNetworkOffline;
   }
 
-  if (consume_quota &&
-      !request_throttler_.RequestQuota(NetworkRequestType::kFeedQuery)) {
+  if (consume_quota && !request_throttler_.RequestQuota(
+                           !is_load_more ? NetworkRequestType::kFeedQuery
+                                         : NetworkRequestType::kNextPage)) {
     return LoadStreamStatus::kCannotLoadFromNetworkThrottled;
   }
 
@@ -620,6 +688,9 @@
 }
 
 RequestMetadata FeedStream::GetRequestMetadata(bool is_for_next_page) const {
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  const Stream* stream = FindStream(kInterestStream);
+  DCHECK(stream);
   RequestMetadata result;
   result.chrome_info = chrome_info_;
   result.display_metrics = delegate_->GetDisplayMetrics();
@@ -631,8 +702,8 @@
     // If we are continuing an existing feed, use whatever session continuity
     // mechanism is currently associated with the stream: client-instance-id
     // for signed-in feed, session_id token for signed-out.
-    DCHECK(model_);
-    if (model_->signed_in()) {
+    DCHECK(stream->model);
+    if (stream->model->signed_in()) {
       result.client_instance_id = GetClientInstanceId();
     } else {
       result.session_id = GetMetadata()->GetSessionIdToken();
@@ -655,7 +726,9 @@
 }
 
 void FeedStream::OnEulaAccepted() {
-  if (surface_updater_->HasSurfaceAttached())
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
+  if (stream.surface_updater->HasSurfaceAttached())
     TriggerStreamLoad();
 }
 
@@ -762,12 +835,16 @@
 }
 
 void FeedStream::LoadModel(std::unique_ptr<StreamModel> model) {
-  DCHECK(!model_);
-  model_ = std::move(model);
-  model_->SetStoreObserver(this);
-  surface_updater_->SetModel(model_.get());
-  offline_page_spy_->SetModel(model_.get());
-  ScheduleModelUnloadIfNoSurfacesAttached();
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  Stream& stream = GetStream(kInterestStream);
+  DCHECK(!stream.model);
+  stream.model = std::move(model);
+  stream.model->SetStoreObserver(this);
+  stream.surface_updater->SetModel(stream.model.get());
+  if (stream.type.IsInterest()) {
+    offline_page_spy_->SetModel(stream.model.get());
+  }
+  ScheduleModelUnloadIfNoSurfacesAttached(stream.type);
 }
 
 void FeedStream::SetRequestSchedule(RequestSchedule schedule) {
@@ -781,50 +858,69 @@
   feed::prefs::SetRequestSchedule(schedule, *profile_prefs_);
 }
 
-void FeedStream::UnloadModel() {
+void FeedStream::UnloadModel(const StreamType& stream_type) {
   // Note: This should only be called from a running Task, as some tasks assume
   // the model remains loaded.
-  if (!model_)
+  Stream* stream = FindStream(stream_type);
+  if (!stream || !stream->model)
     return;
-  offline_page_spy_->SetModel(nullptr);
-  surface_updater_->SetModel(nullptr);
-  model_.reset();
+  if (stream_type.IsInterest()) {
+    offline_page_spy_->SetModel(nullptr);
+  }
+  stream->surface_updater->SetModel(nullptr);
+  stream->model.reset();
 }
-void FeedStream::ReportOpenAction(const std::string& slice_id) {
-  int index = surface_updater_->GetSliceIndexFromSliceId(slice_id);
+void FeedStream::ReportOpenAction(const StreamType& stream_type,
+                                  const std::string& slice_id) {
+  Stream& stream = GetStream(stream_type);
+
+  int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id);
   if (index < 0)
     index = MetricsReporter::kUnknownCardIndex;
   metrics_reporter_->OpenAction(index);
-  notice_card_tracker_.OnOpenAction(index);
+  if (stream_type.IsInterest()) {
+    notice_card_tracker_.OnOpenAction(index);
+  }
 }
 void FeedStream::ReportOpenVisitComplete(base::TimeDelta visit_time) {
   metrics_reporter_->OpenVisitComplete(visit_time);
 }
-void FeedStream::ReportOpenInNewTabAction(const std::string& slice_id) {
-  int index = surface_updater_->GetSliceIndexFromSliceId(slice_id);
+void FeedStream::ReportOpenInNewTabAction(const StreamType& stream_type,
+                                          const std::string& slice_id) {
+  Stream& stream = GetStream(stream_type);
+  int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id);
   if (index < 0)
     index = MetricsReporter::kUnknownCardIndex;
   metrics_reporter_->OpenInNewTabAction(index);
-  notice_card_tracker_.OnOpenAction(index);
+  if (stream_type.IsInterest()) {
+    notice_card_tracker_.OnOpenAction(index);
+  }
 }
 void FeedStream::ReportSliceViewed(SurfaceId surface_id,
+                                   const StreamType& stream_type,
                                    const std::string& slice_id) {
-  int index = surface_updater_->GetSliceIndexFromSliceId(slice_id);
+  Stream& stream = GetStream(stream_type);
+  int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id);
   if (index >= 0) {
-    UpdateShownSlicesUploadCondition(index);
-    notice_card_tracker_.OnSliceViewed(index);
+    if (stream_type.IsInterest()) {
+      UpdateShownSlicesUploadCondition(index);
+      notice_card_tracker_.OnSliceViewed(index);
+    }
     metrics_reporter_->ContentSliceViewed(surface_id, index);
   }
 }
 // TODO(crbug/1147237): Rename this method and related members?
 bool FeedStream::CanUploadActions() const {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   return can_upload_actions_with_notice_card_ ||
          !prefs::GetLastFetchHadNoticeCard(*profile_prefs_);
 }
 void FeedStream::SetLastStreamLoadHadNoticeCard(bool value) {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   prefs::SetLastFetchHadNoticeCard(*profile_prefs_, value);
 }
 bool FeedStream::HasReachedConditionsToUploadActionsWithNoticeCard() {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   if (base::FeatureList::IsEnabled(
           feed::kInterestFeedV2ClicksAndViewsConditionalUpload)) {
     return prefs::GetHasReachedClickAndViewActionsUploadConditions(
@@ -836,6 +932,7 @@
   return true;
 }
 void FeedStream::DeclareHasReachedConditionsToUploadActionsWithNoticeCard() {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   if (base::FeatureList::IsEnabled(
           feed::kInterestFeedV2ClicksAndViewsConditionalUpload)) {
     prefs::SetHasReachedClickAndViewActionsUploadConditions(*profile_prefs_,
@@ -845,23 +942,32 @@
 void FeedStream::UpdateShownSlicesUploadCondition(int viewed_slice_index) {
   constexpr int kShownSlicesThreshold = 2;
 
-  DCHECK(model_) << "Model was unloaded while handling a viewed slice";
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
+  Stream& stream = GetStream(kInterestStream);
+  if (!stream.model) {
+    DLOG(ERROR) << "Model was unloaded while handling a viewed slice";
+    return;
+  }
 
   // Don't take shown slices into consideration when the upload conditions has
   // already been reached.
-  if (HasReachedConditionsToUploadActionsWithNoticeCard())
+  if (HasReachedConditionsToUploadActionsWithNoticeCard()) {
     return;
+  }
 
-  if (!model_->signed_in())
+  if (!stream.model->signed_in()) {
     return;
+  }
 
   if (viewed_slice_index + 1 >= kShownSlicesThreshold)
     DeclareHasReachedConditionsToUploadActionsWithNoticeCard();
 }
 bool FeedStream::CanLogViews() const {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   return CanUploadActions();
 }
 void FeedStream::UpdateCanUploadActionsWithNoticeCard() {
+  // TODO(crbug/1152592): Determine notice card behavior with web feeds.
   can_upload_actions_with_notice_card_ =
       HasReachedConditionsToUploadActionsWithNoticeCard();
 }
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h
index 2b45b09..61c505c 100644
--- a/components/feed/core/v2/feed_stream.h
+++ b/components/feed/core/v2/feed_stream.h
@@ -136,16 +136,21 @@
       base::OnceCallback<void(NetworkResponse)> callback) override;
   void CancelImageFetch(ImageFetchId id) override;
   PersistentKeyValueStoreImpl* GetPersistentKeyValueStore() override;
-  void LoadMore(SurfaceId surface_id,
+  void LoadMore(const SurfaceInterface& surface,
                 base::OnceCallback<void(bool)> callback) override;
   void ExecuteOperations(
+      const StreamType& stream_type,
       std::vector<feedstore::DataOperation> operations) override;
   EphemeralChangeId CreateEphemeralChange(
+      const StreamType& stream_type,
       std::vector<feedstore::DataOperation> operations) override;
   EphemeralChangeId CreateEphemeralChangeFromPackedData(
+      const StreamType& stream_type,
       base::StringPiece data) override;
-  bool CommitEphemeralChange(EphemeralChangeId id) override;
-  bool RejectEphemeralChange(EphemeralChangeId id) override;
+  bool CommitEphemeralChange(const StreamType& stream_type,
+                             EphemeralChangeId id) override;
+  bool RejectEphemeralChange(const StreamType& stream_type,
+                             EphemeralChangeId id) override;
   void ProcessThereAndBackAgain(base::StringPiece data) override;
   void ProcessViewAction(base::StringPiece data) override;
   DebugStreamData GetDebugStreamData() override;
@@ -155,12 +160,15 @@
       const feedui::StreamUpdate& stream_update) override;
 
   void ReportSliceViewed(SurfaceId surface_id,
+                         const StreamType& stream_type,
                          const std::string& slice_id) override;
   void ReportFeedViewed(SurfaceId surface_id) override;
   void ReportPageLoaded() override;
-  void ReportOpenAction(const std::string& slice_id) override;
+  void ReportOpenAction(const StreamType& stream_type,
+                        const std::string& slice_id) override;
   void ReportOpenVisitComplete(base::TimeDelta visit_time) override;
-  void ReportOpenInNewTabAction(const std::string& slice_id) override;
+  void ReportOpenInNewTabAction(const StreamType& stream_type,
+                                const std::string& slice_id) override;
   void ReportStreamScrolled(int distance_dp) override;
   void ReportStreamScrollStart() override;
   void ReportOtherUserAction(FeedUserActionType action_type) override;
@@ -238,7 +246,7 @@
 
   // Unloads the model. Surfaces are not updated, and will remain frozen until a
   // model load is requested.
-  void UnloadModel();
+  void UnloadModel(const StreamType& stream_type);
 
   // Triggers a stream load. The load will be aborted if |ShouldAttemptLoad()|
   // is not true.
@@ -249,7 +257,7 @@
   void FinishClearAll();
 
   // Returns the model if it is loaded, or null otherwise.
-  StreamModel* GetModel() { return model_.get(); }
+  StreamModel* GetModel(const StreamType& stream_type);
 
   RequestMetadata GetRequestMetadata(bool is_for_next_page) const;
 
@@ -274,6 +282,23 @@
  private:
   class OfflineSuggestionsProvider;
 
+  struct Stream {
+    Stream();
+    ~Stream();
+    Stream(const Stream&) = delete;
+    Stream& operator=(const Stream&) = delete;
+    StreamType type;
+    // Whether the model is being loaded. Used to prevent multiple simultaneous
+    // attempts to load the model.
+    bool model_loading_in_progress = false;
+    std::unique_ptr<SurfaceUpdater> surface_updater;
+    // The stream model. Null if not yet loaded.
+    // Internally, this should only be changed by |LoadModel()| and
+    // |UnloadModel()|.
+    std::unique_ptr<StreamModel> model;
+    int unload_on_detach_sequence_number = 0;
+  };
+
   base::WeakPtr<FeedStream> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
@@ -289,9 +314,10 @@
   // To only be called from within a |Task|.
   void ForceRefreshForDebuggingTask();
 
-  void ScheduleModelUnloadIfNoSurfacesAttached();
-  void AddUnloadModelIfNoSurfacesAttachedTask(int sequence_number);
-  void UnloadModelIfNoSurfacesAttachedTask();
+  void ScheduleModelUnloadIfNoSurfacesAttached(const StreamType& stream_type);
+  void AddUnloadModelIfNoSurfacesAttachedTask(const StreamType& stream_type,
+                                              int sequence_number);
+  void UnloadModelIfNoSurfacesAttachedTask(const StreamType& stream_type);
 
   void InitialStreamLoadComplete(LoadStreamTask::Result result);
   void LoadMoreComplete(LoadMoreTask::Result result);
@@ -311,6 +337,10 @@
 
   void UpdateCanUploadActionsWithNoticeCard();
 
+  Stream& GetStream(const StreamType& type);
+  Stream* FindStream(const StreamType& type);
+  const Stream* FindStream(const StreamType& type) const;
+
   // Unowned.
 
   offline_pages::PrefetchService* prefetch_service_;
@@ -327,23 +357,18 @@
   ChromeInfo chrome_info_;
 
   offline_pages::TaskQueue task_queue_;
-  // Whether the model is being loaded. Used to prevent multiple simultaneous
-  // attempts to load the model.
-  bool model_loading_in_progress_ = false;
-  std::unique_ptr<SurfaceUpdater> surface_updater_;
+
+  std::map<StreamType, Stream> streams_;
+
   std::unique_ptr<OfflineSuggestionsProvider> offline_suggestions_provider_;
   std::unique_ptr<OfflinePageSpy> offline_page_spy_;
-  // The stream model. Null if not yet loaded.
-  // Internally, this should only be changed by |LoadModel()| and
-  // |UnloadModel()|.
-  std::unique_ptr<StreamModel> model_;
 
   // Mutable state.
   RequestThrottler request_throttler_;
   base::TimeTicks signed_out_refreshes_until_;
   std::vector<base::OnceCallback<void(bool)>> load_more_complete_callbacks_;
   Metadata metadata_;
-  int unload_on_detach_sequence_number_ = 0;
+
   bool is_activity_logging_enabled_ = false;
   // Whether the feed stream can upload actions with the notice card in the
   // feed.
diff --git a/components/feed/core/v2/feed_stream_unittest.cc b/components/feed/core/v2/feed_stream_unittest.cc
index 07d3c5e..7157842 100644
--- a/components/feed/core/v2/feed_stream_unittest.cc
+++ b/components/feed/core/v2/feed_stream_unittest.cc
@@ -166,7 +166,8 @@
  public:
   // Provide some helper functionality to attach/detach the surface.
   // This way we can auto-detach in the destructor.
-  explicit TestSurface(FeedStream* stream = nullptr) {
+  explicit TestSurface(FeedStream* stream = nullptr)
+      : FeedStream::SurfaceInterface(kInterestStream) {
     if (stream)
       Attach(stream);
   }
@@ -305,6 +306,7 @@
  public:
   // FeedNetwork implementation.
   void SendQueryRequest(
+      NetworkRequestType request_type,
       const feedwire::Request& request,
       bool force_signed_out_request,
       base::OnceCallback<void(QueryRequestResult)> callback) override {
@@ -658,7 +660,7 @@
 
   void UnloadModel() {
     WaitForIdleTaskQueue();
-    stream_->UnloadModel();
+    stream_->UnloadModel(kInterestStream);
   }
 
   // Dumps the state of |FeedStore| to a string for debugging.
@@ -759,7 +761,7 @@
   EXPECT_EQ(LoadStreamStatus::kLoadedFromNetwork,
             metrics_reporter_->background_refresh_status);
   EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
-  EXPECT_FALSE(stream_->GetModel());
+  EXPECT_FALSE(stream_->GetModel(kInterestStream));
   TestSurface surface(stream_.get());
   WaitForIdleTaskQueue();
   EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates());
@@ -864,12 +866,13 @@
   }
   TestSurface surface(stream_.get());
   // Remove #1, add #2.
-  stream_->ExecuteOperations({
-      MakeOperation(MakeRemove(MakeClusterId(1))),
-      MakeOperation(MakeCluster(2, MakeRootId())),
-      MakeOperation(MakeContentNode(2, MakeClusterId(2))),
-      MakeOperation(MakeContent(2)),
-  });
+  stream_->ExecuteOperations(
+      kInterestStream, {
+                           MakeOperation(MakeRemove(MakeClusterId(1))),
+                           MakeOperation(MakeCluster(2, MakeRootId())),
+                           MakeOperation(MakeContentNode(2, MakeClusterId(2))),
+                           MakeOperation(MakeContent(2)),
+                       });
   ASSERT_TRUE(surface.update);
   const feedui::StreamUpdate& initial_state = surface.initial_state.value();
   const feedui::StreamUpdate& update = surface.update.value();
@@ -892,18 +895,20 @@
   }
   TestSurface surface(stream_.get());
   // Add #2.
-  stream_->ExecuteOperations({
-      MakeOperation(MakeCluster(2, MakeRootId())),
-      MakeOperation(MakeContentNode(2, MakeClusterId(2))),
-      MakeOperation(MakeContent(2)),
-  });
+  stream_->ExecuteOperations(
+      kInterestStream, {
+                           MakeOperation(MakeCluster(2, MakeRootId())),
+                           MakeOperation(MakeContentNode(2, MakeClusterId(2))),
+                           MakeOperation(MakeContent(2)),
+                       });
 
   // Clear the last update and add #3.
-  stream_->ExecuteOperations({
-      MakeOperation(MakeCluster(3, MakeRootId())),
-      MakeOperation(MakeContentNode(3, MakeClusterId(3))),
-      MakeOperation(MakeContent(3)),
-  });
+  stream_->ExecuteOperations(
+      kInterestStream, {
+                           MakeOperation(MakeCluster(3, MakeRootId())),
+                           MakeOperation(MakeContentNode(3, MakeClusterId(3))),
+                           MakeOperation(MakeContent(3)),
+                       });
 
   // The last update should have only one new piece of content.
   // This verifies the current content set is tracked properly.
@@ -925,10 +930,11 @@
   WaitForIdleTaskQueue();
 
   // Remove both pieces of content.
-  stream_->ExecuteOperations({
-      MakeOperation(MakeRemove(MakeClusterId(0))),
-      MakeOperation(MakeRemove(MakeClusterId(1))),
-  });
+  stream_->ExecuteOperations(kInterestStream,
+                             {
+                                 MakeOperation(MakeRemove(MakeClusterId(0))),
+                                 MakeOperation(MakeRemove(MakeClusterId(1))),
+                             });
 
   ASSERT_EQ("loading -> 2 slices -> no-cards", surface.DescribeUpdates());
 }
@@ -945,9 +951,10 @@
   surface.Clear();
 
   // Arbitrary stream change. Surface should not see the update.
-  stream_->ExecuteOperations({
-      MakeOperation(MakeRemove(MakeClusterId(1))),
-  });
+  stream_->ExecuteOperations(kInterestStream,
+                             {
+                                 MakeOperation(MakeRemove(MakeClusterId(1))),
+                             });
   EXPECT_FALSE(surface.update);
 }
 
@@ -974,8 +981,9 @@
 
   EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates());
   // Verify the model is filled correctly.
-  EXPECT_STRINGS_EQUAL(ModelStateFor(MakeTypicalInitialModelState()),
-                       stream_->GetModel()->DumpStateForTesting());
+  EXPECT_STRINGS_EQUAL(
+      ModelStateFor(MakeTypicalInitialModelState()),
+      stream_->GetModel(kInterestStream)->DumpStateForTesting());
   // Verify the data was written to the store.
   EXPECT_STRINGS_EQUAL(ModelStateFor(MakeTypicalInitialModelState()),
                        ModelStateFor(store_.get()));
@@ -1059,7 +1067,7 @@
   WaitForIdleTaskQueue();
   ASSERT_EQ(1, network_.send_query_call_count);
   surface.Detach();
-  stream_->UnloadModel();
+  stream_->UnloadModel(surface.GetStreamType());
 
   // Ensure a refresh is foreced only after a scheduled refresh was missed.
   // First, load the stream after 11 seconds.
@@ -1072,7 +1080,7 @@
   // Load the stream after 13 seconds. We missed the scheduled refresh at
   // 12 seconds.
   surface.Detach();
-  stream_->UnloadModel();
+  stream_->UnloadModel(surface.GetStreamType());
   task_environment_.AdvanceClock(base::TimeDelta::FromSeconds(2));
   surface.Attach(stream_.get());
   WaitForIdleTaskQueue();
@@ -1280,16 +1288,16 @@
   // Validate the downstream consumption of the response.
   EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates());
   EXPECT_EQ(kSessionId, stream_->GetMetadata()->GetSessionIdToken());
-  EXPECT_FALSE(stream_->GetModel()->signed_in());
+  EXPECT_FALSE(stream_->GetModel(surface.GetStreamType())->signed_in());
 
   // Advance the clock beyond the forced signed out period.
   task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2));
-  EXPECT_FALSE(stream_->GetModel()->signed_in());
+  EXPECT_FALSE(stream_->GetModel(surface.GetStreamType())->signed_in());
 
   // Requests for subsequent pages continue the use existing session.
   // Subsequent responses may omit the session id.
   response_translator_.InjectResponse(model_generator.MakeNextPage());
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
 
   // Validate that the network request was sent as signed out and
@@ -1304,7 +1312,7 @@
             kSessionId);
 
   // The model should still be in the signed-out state.
-  EXPECT_FALSE(stream_->GetModel()->signed_in());
+  EXPECT_FALSE(stream_->GetModel(kInterestStream)->signed_in());
 
   // Force a refresh of the feed by clearing the cache. The request for the
   // first page should revert back to signed-in. The response data will denote
@@ -1319,7 +1327,7 @@
   EXPECT_FALSE(network_.forced_signed_out_request);
 
   // The model should now be in the signed-in state.
-  EXPECT_TRUE(stream_->GetModel()->signed_in());
+  EXPECT_TRUE(stream_->GetModel(kInterestStream)->signed_in());
   EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty());
 }
 
@@ -1360,8 +1368,9 @@
   ASSERT_EQ("loading -> 2 slices", surface.DescribeUpdates());
   EXPECT_FALSE(network_.query_request_sent);
   // Verify the model is filled correctly.
-  EXPECT_STRINGS_EQUAL(ModelStateFor(MakeTypicalInitialModelState()),
-                       stream_->GetModel()->DumpStateForTesting());
+  EXPECT_STRINGS_EQUAL(
+      ModelStateFor(MakeTypicalInitialModelState()),
+      stream_->GetModel(kInterestStream)->DumpStateForTesting());
 }
 
 TEST_F(FeedStreamTest, LoadingSpinnerIsSentInitially) {
@@ -1412,7 +1421,7 @@
       MakeOperation(MakeContentNode(2, MakeClusterId(2))),
       MakeOperation(MakeContent(2)),
   };
-  stream_->ExecuteOperations(operations);
+  stream_->ExecuteOperations(kInterestStream, operations);
 
   WaitForIdleTaskQueue();
 
@@ -1437,7 +1446,7 @@
       MakeOperation(MakeContentNode(3, MakeClusterId(3))),
       MakeOperation(MakeContent(3)),
   };
-  stream_->ExecuteOperations(operations2);
+  stream_->ExecuteOperations(surface.GetStreamType(), operations2);
 
   WaitForIdleTaskQueue();
   EXPECT_STRINGS_EQUAL(
@@ -1452,7 +1461,7 @@
   WaitForIdleTaskQueue();
 
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
   EXPECT_EQ(1, metrics_reporter_->slice_viewed_index);
 }
@@ -1467,7 +1476,7 @@
   // Load page 2.
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
   // Ensure metrics reporter was informed at the start of the operation.
   EXPECT_EQ(surface.GetSurfaceId(), metrics_reporter_->load_more_surface_id);
   WaitForIdleTaskQueue();
@@ -1477,7 +1486,7 @@
 
   // Load page 3.
   response_translator_.InjectResponse(MakeTypicalNextPageState(3));
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
 
   WaitForIdleTaskQueue();
   ASSERT_EQ(base::Optional<bool>(true), callback.GetResult());
@@ -1494,14 +1503,15 @@
   // Load page 2.
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
 
   WaitForIdleTaskQueue();
   ASSERT_EQ(base::Optional<bool>(true), callback.GetResult());
 
   // Verify stored state is equivalent to in-memory model.
-  EXPECT_STRINGS_EQUAL(stream_->GetModel()->DumpStateForTesting(),
-                       ModelStateFor(store_.get()));
+  EXPECT_STRINGS_EQUAL(
+      stream_->GetModel(kInterestStream)->DumpStateForTesting(),
+      ModelStateFor(store_.get()));
 }
 
 TEST_F(FeedStreamTest, LoadMorePersistAndLoadMore) {
@@ -1515,7 +1525,7 @@
   // Load page 2.
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
   WaitForIdleTaskQueue();
   ASSERT_EQ(base::Optional<bool>(true), callback.GetResult());
 
@@ -1528,14 +1538,15 @@
   WaitForIdleTaskQueue();
   callback.Clear();
   surface.Clear();
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
   WaitForIdleTaskQueue();
 
   ASSERT_EQ(base::Optional<bool>(true), callback.GetResult());
   ASSERT_EQ("4 slices +spinner -> 6 slices", surface.DescribeUpdates());
   // Verify stored state is equivalent to in-memory model.
-  EXPECT_STRINGS_EQUAL(stream_->GetModel()->DumpStateForTesting(),
-                       ModelStateFor(store_.get()));
+  EXPECT_STRINGS_EQUAL(
+      stream_->GetModel(surface.GetStreamType())->DumpStateForTesting(),
+      ModelStateFor(store_.get()));
 }
 
 TEST_F(FeedStreamTest, LoadMoreSendsTokens) {
@@ -1547,7 +1558,7 @@
   stream_->GetMetadata()->SetConsistencyToken("token-1");
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
 
   WaitForIdleTaskQueue();
   ASSERT_EQ("2 slices +spinner -> 4 slices", surface.DescribeUpdates());
@@ -1563,7 +1574,7 @@
 
   stream_->GetMetadata()->SetConsistencyToken("token-2");
   response_translator_.InjectResponse(MakeTypicalNextPageState(3));
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
 
   WaitForIdleTaskQueue();
   ASSERT_EQ("4 slices +spinner -> 6 slices", surface.DescribeUpdates());
@@ -1589,7 +1600,7 @@
   WaitForIdleTaskQueue();
 
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
   WaitForIdleTaskQueue();
 
   // LoadMore fails, and does not make an additional request.
@@ -1610,7 +1621,7 @@
   // Don't inject another response, which results in a proto translation
   // failure.
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
   WaitForIdleTaskQueue();
 
   EXPECT_EQ(base::Optional<bool>(false), callback.GetResult());
@@ -1626,14 +1637,15 @@
   // Use a different initial state (which includes a CLEAR_ALL).
   response_translator_.InjectResponse(MakeTypicalInitialModelState(5));
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+  stream_->LoadMore(surface, callback.Bind());
 
   WaitForIdleTaskQueue();
   ASSERT_EQ(base::Optional<bool>(true), callback.GetResult());
 
   // Verify stored state is equivalent to in-memory model.
-  EXPECT_STRINGS_EQUAL(stream_->GetModel()->DumpStateForTesting(),
-                       ModelStateFor(store_.get()));
+  EXPECT_STRINGS_EQUAL(
+      stream_->GetModel(surface.GetStreamType())->DumpStateForTesting(),
+      ModelStateFor(store_.get()));
 
   // Verify the new state has been pushed to |surface|.
   ASSERT_EQ("2 slices +spinner -> 2 slices", surface.DescribeUpdates());
@@ -1654,7 +1666,8 @@
 
 TEST_F(FeedStreamTest, LoadMoreBeforeLoad) {
   CallbackReceiver<bool> callback;
-  stream_->LoadMore(SurfaceId(), callback.Bind());
+  TestSurface surface;
+  stream_->LoadMore(surface, callback.Bind());
 
   EXPECT_EQ(base::Optional<bool>(false), callback.GetResult());
 }
@@ -1720,7 +1733,7 @@
   TestSurface surface(stream_.get());
   WaitForIdleTaskQueue();
 
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
   response_translator_.InjectResponse(MakeTypicalInitialModelState());
   stream_->OnCacheDataCleared();  // triggers ClearAll().
@@ -1871,7 +1884,7 @@
 
   // Reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   // Verify that the view action is still dropped because we haven't
@@ -1902,7 +1915,7 @@
 
   // Reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   // Attach a new surface to update the bit to enable uploads.
@@ -1911,7 +1924,7 @@
   // Trigger an upload through load more to isolate the effect of the on-attach
   // event on enabling uploads.
   response_translator_.InjectResponse(MakeTypicalNextPageState());
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
 
   // Verify that the ThereAndBackAgain action was uploaded.
@@ -1931,7 +1944,7 @@
 
   // Reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   stream_->OnEnterBackground();
@@ -1975,7 +1988,7 @@
 
   // Reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   // Assert that uploads are not yet enabled.
@@ -1996,7 +2009,7 @@
 
   // Reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   // Update the upload enable bits which will enable upload.
@@ -2049,7 +2062,7 @@
 
   // Try to reach conditions.
   stream_->ReportSliceViewed(
-      surface.GetSurfaceId(),
+      surface.GetSurfaceId(), surface.GetStreamType(),
       surface.initial_state->updated_slices(1).slice().slice_id());
 
   // Try to trigger an upload through a query.
@@ -2114,7 +2127,7 @@
 
   network_.consistency_token = "token-12";
 
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
 
   EXPECT_EQ(1, network_.action_request_sent->feed_actions_size());
@@ -2144,7 +2157,7 @@
             MakeTypicalNextPageState(page++, kTestTimeEpoch, signed_in, waa_on,
                                      privacy_notice_fulfilled));
         CallbackReceiver<bool> callback;
-        stream_->LoadMore(surface.GetSurfaceId(), callback.Bind());
+        stream_->LoadMore(surface, callback.Bind());
         WaitForIdleTaskQueue();
         EXPECT_EQ(
             stream_->IsActivityLoggingEnabled(),
@@ -2178,7 +2191,7 @@
   // effect of load more.
   base::HistogramTester histograms;
 
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
 
   // Process a view action that should be dropped because the upload of actions
@@ -2342,11 +2355,11 @@
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(999));
   WaitForIdleTaskQueue();
-  EXPECT_TRUE(stream_->GetModel());
+  EXPECT_TRUE(stream_->GetModel(surface.GetStreamType()));
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(2));
   WaitForIdleTaskQueue();
-  EXPECT_FALSE(stream_->GetModel());
+  EXPECT_FALSE(stream_->GetModel(surface.GetStreamType()));
 }
 
 TEST_F(FeedStreamTest, ModelDoesNotUnloadIfSurfaceIsAttached) {
@@ -2362,13 +2375,13 @@
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(999));
   WaitForIdleTaskQueue();
-  EXPECT_TRUE(stream_->GetModel());
+  EXPECT_TRUE(stream_->GetModel(surface.GetStreamType()));
 
   surface.Attach(stream_.get());
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(2));
   WaitForIdleTaskQueue();
-  EXPECT_TRUE(stream_->GetModel());
+  EXPECT_TRUE(stream_->GetModel(surface.GetStreamType()));
 }
 
 TEST_F(FeedStreamTest, ModelUnloadsAfterSecondTimeout) {
@@ -2384,7 +2397,7 @@
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(999));
   WaitForIdleTaskQueue();
-  EXPECT_TRUE(stream_->GetModel());
+  EXPECT_TRUE(stream_->GetModel(surface.GetStreamType()));
 
   // Attaching another surface will prolong the unload time for another second.
   surface.Attach(stream_.get());
@@ -2392,11 +2405,11 @@
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(999));
   WaitForIdleTaskQueue();
-  EXPECT_TRUE(stream_->GetModel());
+  EXPECT_TRUE(stream_->GetModel(surface.GetStreamType()));
 
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(2));
   WaitForIdleTaskQueue();
-  EXPECT_FALSE(stream_->GetModel());
+  EXPECT_FALSE(stream_->GetModel(surface.GetStreamType()));
 }
 
 TEST_F(FeedStreamTest, ProvidesPrefetchSuggestionsWhenModelLoaded) {
@@ -2437,7 +2450,7 @@
       callback.Bind());
   WaitForIdleTaskQueue();
 
-  ASSERT_FALSE(stream_->GetModel());
+  ASSERT_FALSE(stream_->GetModel(kInterestStream));
   ASSERT_TRUE(callback.GetResult());
   const std::vector<offline_pages::PrefetchSuggestion>& suggestions =
       callback.GetResult().value();
@@ -2550,7 +2563,7 @@
   TestSurface surface(stream_.get());
   WaitForIdleTaskQueue();
 
-  stream_->UnloadModel();
+  stream_->UnloadModel(surface.GetStreamType());
 
   // Offline badge no longer present.
   EXPECT_EQ((std::map<std::string, std::string>()),
@@ -2606,7 +2619,7 @@
 
   // LoadMore, and verify the same token is used.
   response_translator_.InjectResponse(MakeTypicalNextPageState(2));
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
 
   ASSERT_EQ(2, network_.send_query_call_count);
@@ -2625,7 +2638,7 @@
   stream_->OnSignedOut();
   WaitForIdleTaskQueue();
 
-  EXPECT_FALSE(stream_->GetModel());
+  EXPECT_FALSE(stream_->GetModel(surface.GetStreamType()));
   const bool is_for_next_page = false;  // No model so no first page yet.
   const std::string new_instance_id =
       stream_->GetRequestMetadata(is_for_next_page).client_instance_id;
@@ -2649,14 +2662,17 @@
       surface.initial_state->updated_slices(notice_card_index)
           .slice()
           .slice_id();
-  stream_->ReportSliceViewed(surface.GetSurfaceId(), slice_id);
-  stream_->ReportSliceViewed(surface.GetSurfaceId(), slice_id);
-  stream_->ReportSliceViewed(surface.GetSurfaceId(), slice_id);
-  stream_->ReportOpenAction(slice_id);
+  stream_->ReportSliceViewed(surface.GetSurfaceId(), surface.GetStreamType(),
+                             slice_id);
+  stream_->ReportSliceViewed(surface.GetSurfaceId(), surface.GetStreamType(),
+                             slice_id);
+  stream_->ReportSliceViewed(surface.GetSurfaceId(), surface.GetStreamType(),
+                             slice_id);
+  stream_->ReportOpenAction(surface.GetStreamType(), slice_id);
 
   response_translator_.InjectResponse(MakeTypicalInitialModelState());
   refresh_scheduler_.Clear();
-  stream_->UnloadModel();
+  stream_->UnloadModel(surface.GetStreamType());
   stream_->ExecuteRefreshTask();
   WaitForIdleTaskQueue();
 
@@ -2749,7 +2765,7 @@
   task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
   response_translator_.InjectResponse(model_generator.MakeNextPage(2),
                                       kSessionToken1);
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
   ASSERT_EQ(2, network_.send_query_call_count);
   EXPECT_TRUE(network_.query_request_sent->feed_request()
@@ -2772,7 +2788,7 @@
   //     - the session-id's expiry time should be unchanged
   task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
   response_translator_.InjectResponse(model_generator.MakeNextPage(3));
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
   ASSERT_EQ(3, network_.send_query_call_count);
   EXPECT_TRUE(network_.query_request_sent->feed_request()
@@ -2796,7 +2812,7 @@
   task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
   response_translator_.InjectResponse(model_generator.MakeNextPage(4),
                                       kSessionToken2);
-  stream_->LoadMore(surface.GetSurfaceId(), base::DoNothing());
+  stream_->LoadMore(surface, base::DoNothing());
   WaitForIdleTaskQueue();
   ASSERT_EQ(4, network_.send_query_call_count);
   EXPECT_TRUE(network_.query_request_sent->feed_request()
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc
index 5ed284b..e63bab94 100644
--- a/components/feed/core/v2/metrics_reporter.cc
+++ b/components/feed/core/v2/metrics_reporter.cc
@@ -425,6 +425,11 @@
           "ContentSuggestions.Feed.Network.ResponseStatus.UploadActions",
           http_status_code);
       return;
+    case NetworkRequestType::kNextPage:
+      base::UmaHistogramSparse(
+          "ContentSuggestions.Feed.Network.ResponseStatus.NextPage",
+          http_status_code);
+      return;
   }
 }
 
diff --git a/components/feed/core/v2/notice_card_tracker.cc b/components/feed/core/v2/notice_card_tracker.cc
index cb318c86..99d8a42 100644
--- a/components/feed/core/v2/notice_card_tracker.cc
+++ b/components/feed/core/v2/notice_card_tracker.cc
@@ -69,8 +69,9 @@
 }
 
 bool NoticeCardTracker::HasNoticeCardActionsCountPrerequisites(int index) {
-  if (!base::FeatureList::IsEnabled(feed::kInterestFeedNoticeCardAutoDismiss))
+  if (!base::FeatureList::IsEnabled(feed::kInterestFeedNoticeCardAutoDismiss)) {
     return false;
+  }
 
   if (!prefs::GetLastFetchHadNoticeCard(*profile_prefs_)) {
     return false;
@@ -94,4 +95,4 @@
   prefs::IncrementNoticeCardClicksCount(*profile_prefs_);
 }
 
-}  // namespace feed
\ No newline at end of file
+}  // namespace feed
diff --git a/components/feed/core/v2/public/feed_stream_api.cc b/components/feed/core/v2/public/feed_stream_api.cc
index 87c23e7b..b5216278 100644
--- a/components/feed/core/v2/public/feed_stream_api.cc
+++ b/components/feed/core/v2/public/feed_stream_api.cc
@@ -9,7 +9,8 @@
 FeedStreamApi::FeedStreamApi() = default;
 FeedStreamApi::~FeedStreamApi() = default;
 
-FeedStreamApi::SurfaceInterface::SurfaceInterface() {
+FeedStreamApi::SurfaceInterface::SurfaceInterface(StreamType stream_type)
+    : stream_type_(stream_type) {
   static SurfaceId::Generator id_generator;
   surface_id_ = id_generator.GenerateNextId();
 }
diff --git a/components/feed/core/v2/public/feed_stream_api.h b/components/feed/core/v2/public/feed_stream_api.h
index 1c2c272..65a9137 100644
--- a/components/feed/core/v2/public/feed_stream_api.h
+++ b/components/feed/core/v2/public/feed_stream_api.h
@@ -26,26 +26,57 @@
 namespace feed {
 class PersistentKeyValueStore;
 
+// Selects the stream type.
+// TODO(crbug.com/1152592): Need to use StreamType in several places:
+// - Stream loading/saving
+// - Metrics
+// Note: currently there are two options, but this leaves room for more
+// parameters.
+class StreamType {
+ public:
+  enum class Type {
+    kInterest,
+    kWebFeed,
+  };
+  constexpr explicit StreamType(Type t) : type(t) {}
+  bool operator<(const StreamType& rhs) const { return type < rhs.type; }
+  bool operator==(const StreamType& rhs) const { return type == rhs.type; }
+
+  bool IsInterest() const { return type == Type::kInterest; }
+  bool IsWebFeed() const { return type == Type::kWebFeed; }
+
+ private:
+  Type type = Type::kInterest;
+};
+
+constexpr StreamType kInterestStream(StreamType::Type::kInterest);
+
 // This is the public access point for interacting with the Feed stream
 // contents.
 class FeedStreamApi {
  public:
   class SurfaceInterface : public base::CheckedObserver {
    public:
-    SurfaceInterface();
+    explicit SurfaceInterface(StreamType type);
     ~SurfaceInterface() override;
-    // Called after registering the observer to provide the full stream state.
-    // Also called whenever the stream changes.
-    virtual void StreamUpdate(const feedui::StreamUpdate&) = 0;
+
     // Returns a unique ID for the surface. The ID will not be reused until
     // after the Chrome process is closed.
     SurfaceId GetSurfaceId() const;
 
+    // Returns the `StreamType` this `SurfaceInterface` requests.
+    StreamType GetStreamType() const { return stream_type_; }
+
+    // Called after registering the observer to provide the full stream state.
+    // Also called whenever the stream changes.
+    virtual void StreamUpdate(const feedui::StreamUpdate&) = 0;
+
     virtual void ReplaceDataStoreEntry(base::StringPiece key,
                                        base::StringPiece data) = 0;
     virtual void RemoveDataStoreEntry(base::StringPiece key) = 0;
 
    private:
+    StreamType stream_type_;
     SurfaceId surface_id_;
   };
 
@@ -80,7 +111,7 @@
   // Calls |callback| when complete. If no content could be added, the parameter
   // is false, and the caller should expect |LoadMore| to fail if called
   // further.
-  virtual void LoadMore(SurfaceId surface,
+  virtual void LoadMore(const SurfaceInterface& surface,
                         base::OnceCallback<void(bool)> callback) = 0;
 
   // Request to fetch and image for use in the feed. Calls |callback|
@@ -99,20 +130,25 @@
   // Apply |operations| to the stream model. Does nothing if the model is not
   // yet loaded.
   virtual void ExecuteOperations(
+      const StreamType& stream_type,
       std::vector<feedstore::DataOperation> operations) = 0;
 
   // Create a temporary change that may be undone or committed later. Does
   // nothing if the model is not yet loaded.
   virtual EphemeralChangeId CreateEphemeralChange(
+      const StreamType& stream_type,
       std::vector<feedstore::DataOperation> operations) = 0;
   // Same as |CreateEphemeralChange()|, but data is a serialized
   // |feedpacking::DismissData| message.
   virtual EphemeralChangeId CreateEphemeralChangeFromPackedData(
+      const StreamType& stream_type,
       base::StringPiece data) = 0;
   // Commits a change. Returns false if the change does not exist.
-  virtual bool CommitEphemeralChange(EphemeralChangeId id) = 0;
+  virtual bool CommitEphemeralChange(const StreamType& stream_type,
+                                     EphemeralChangeId id) = 0;
   // Rejects a change. Returns false if the change does not exist.
-  virtual bool RejectEphemeralChange(EphemeralChangeId id) = 0;
+  virtual bool RejectEphemeralChange(const StreamType& stream_type,
+                                     EphemeralChangeId id) = 0;
 
   // Sends 'ThereAndBackAgainData' back to the server. |data| is a serialized
   // |feedwire::ThereAndBackAgainData| message.
@@ -127,6 +163,7 @@
   // A slice was viewed (2/3rds of it is in the viewport). Should be called
   // once for each viewed slice in the stream.
   virtual void ReportSliceViewed(SurfaceId surface_id,
+                                 const StreamType& stream_type,
                                  const std::string& slice_id) = 0;
   // Some feed content has been loaded and is now available to the user on the
   // feed surface. Reported only once after a surface is attached.
@@ -134,13 +171,15 @@
   // A web page was loaded in response to opening a link from the Feed.
   virtual void ReportPageLoaded() = 0;
   // The user triggered the default open action, usually by tapping the card.
-  virtual void ReportOpenAction(const std::string& slice_id) = 0;
+  virtual void ReportOpenAction(const StreamType& stream_type,
+                                const std::string& slice_id) = 0;
   // The user triggered an open action, visited a web page, and then navigated
   // away or backgrouded the tab. |visit_time| is a measure of how long the
   // visited page was foregrounded.
   virtual void ReportOpenVisitComplete(base::TimeDelta visit_time) = 0;
   // The user triggered the 'open in new tab' action.
-  virtual void ReportOpenInNewTabAction(const std::string& slice_id) = 0;
+  virtual void ReportOpenInNewTabAction(const StreamType& stream_type,
+                                        const std::string& slice_id) = 0;
   // The user scrolled the feed by |distance_dp| and then stopped.
   virtual void ReportStreamScrolled(int distance_dp) = 0;
   // The user started scrolling the feed. Typically followed by a call to
diff --git a/components/feed/core/v2/request_throttler.cc b/components/feed/core/v2/request_throttler.cc
index cddfdc3..4c237769 100644
--- a/components/feed/core/v2/request_throttler.cc
+++ b/components/feed/core/v2/request_throttler.cc
@@ -18,6 +18,8 @@
       return GetFeedConfig().max_feed_query_requests_per_day;
     case NetworkRequestType::kUploadActions:
       return GetFeedConfig().max_action_upload_requests_per_day;
+    case NetworkRequestType::kNextPage:
+      return GetFeedConfig().max_next_page_requests_per_day;
   }
 }
 
diff --git a/components/feed/core/v2/tasks/clear_all_task.cc b/components/feed/core/v2/tasks/clear_all_task.cc
index 45fb2c7..95619a4 100644
--- a/components/feed/core/v2/tasks/clear_all_task.cc
+++ b/components/feed/core/v2/tasks/clear_all_task.cc
@@ -17,7 +17,8 @@
 ClearAllTask::~ClearAllTask() = default;
 
 void ClearAllTask::Run() {
-  stream_->UnloadModel();
+  // TODO(crbug/1152592): Need to unload all models here.
+  stream_->UnloadModel(kInterestStream);
   stream_->GetPersistentKeyValueStore()->ClearAll(base::DoNothing());
   stream_->GetStore()->ClearAll(
       base::BindOnce(&ClearAllTask::StoreClearComplete, GetWeakPtr()));
diff --git a/components/feed/core/v2/tasks/get_prefetch_suggestions_task.cc b/components/feed/core/v2/tasks/get_prefetch_suggestions_task.cc
index 69cf3fb4..57a076d 100644
--- a/components/feed/core/v2/tasks/get_prefetch_suggestions_task.cc
+++ b/components/feed/core/v2/tasks/get_prefetch_suggestions_task.cc
@@ -50,8 +50,8 @@
 GetPrefetchSuggestionsTask::~GetPrefetchSuggestionsTask() = default;
 
 void GetPrefetchSuggestionsTask::Run() {
-  if (stream_->GetModel()) {
-    PullSuggestionsFromModel(*stream_->GetModel());
+  if (stream_->GetModel(kInterestStream)) {
+    PullSuggestionsFromModel(*stream_->GetModel(kInterestStream));
     return;
   }
 
diff --git a/components/feed/core/v2/tasks/load_more_task.cc b/components/feed/core/v2/tasks/load_more_task.cc
index 19509e53..8e57766 100644
--- a/components/feed/core/v2/tasks/load_more_task.cc
+++ b/components/feed/core/v2/tasks/load_more_task.cc
@@ -30,7 +30,8 @@
 
 void LoadMoreTask::Run() {
   // Check prerequisites.
-  StreamModel* model = stream_->GetModel();
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  StreamModel* model = stream_->GetModel(kInterestStream);
   if (!model)
     return Done(LoadStreamStatus::kLoadMoreModelIsNotLoaded);
 
@@ -46,7 +47,8 @@
 }
 
 void LoadMoreTask::UploadActionsComplete(UploadActionsTask::Result result) {
-  StreamModel* model = stream_->GetModel();
+  // TODO(crbug/1152592): Parameterize stream loading by stream type.
+  StreamModel* model = stream_->GetModel(kInterestStream);
   DCHECK(model) << "Model was unloaded outside of a Task";
 
   // Determine whether the load more request should be forced signed-out
@@ -65,17 +67,18 @@
   // Send network request.
   fetch_start_time_ = base::TimeTicks::Now();
   stream_->GetNetwork()->SendQueryRequest(
+      NetworkRequestType::kNextPage,
       CreateFeedQueryLoadMoreRequest(
           stream_->GetRequestMetadata(/*is_for_next_page=*/true),
           stream_->GetMetadata()->GetConsistencyToken(),
-          stream_->GetModel()->GetNextPageToken()),
+          stream_->GetModel(kInterestStream)->GetNextPageToken()),
       force_signed_out_request,
       base::BindOnce(&LoadMoreTask::QueryRequestComplete, GetWeakPtr()));
 }
 
 void LoadMoreTask::QueryRequestComplete(
     FeedNetwork::QueryRequestResult result) {
-  StreamModel* model = stream_->GetModel();
+  StreamModel* model = stream_->GetModel(kInterestStream);
   DCHECK(model) << "Model was unloaded outside of a Task";
 
   if (!result.response_body)
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc
index 5eb66ed6..89802de 100644
--- a/components/feed/core/v2/tasks/load_stream_task.cc
+++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -131,6 +131,7 @@
       std::make_unique<UploadActionsTask::Result>(std::move(result));
   latencies_->StepComplete(LoadLatencyTimes::kUploadActions);
   stream_->GetNetwork()->SendQueryRequest(
+      NetworkRequestType::kFeedQuery,
       CreateFeedQueryRefreshRequest(
           GetRequestReason(load_type_),
           stream_->GetRequestMetadata(/*is_for_next_page=*/false),
@@ -143,7 +144,7 @@
     FeedNetwork::QueryRequestResult result) {
   latencies_->StepComplete(LoadLatencyTimes::kQueryRequest);
 
-  DCHECK(!stream_->GetModel());
+  DCHECK(!stream_->GetModel(kInterestStream));
 
   network_response_info_ = result.response_info;
 
diff --git a/components/feed/core/v2/tasks/prefetch_images_task.cc b/components/feed/core/v2/tasks/prefetch_images_task.cc
index 7131229..b8b5b52 100644
--- a/components/feed/core/v2/tasks/prefetch_images_task.cc
+++ b/components/feed/core/v2/tasks/prefetch_images_task.cc
@@ -38,8 +38,8 @@
 PrefetchImagesTask::~PrefetchImagesTask() = default;
 
 void PrefetchImagesTask::Run() {
-  if (stream_->GetModel()) {
-    PrefetchImagesFromModel(*stream_->GetModel());
+  if (stream_->GetModel(kInterestStream)) {
+    PrefetchImagesFromModel(*stream_->GetModel(kInterestStream));
     return;
   }
 
diff --git a/components/full_restore/window_info.h b/components/full_restore/window_info.h
index 86feaadc..235e241 100644
--- a/components/full_restore/window_info.h
+++ b/components/full_restore/window_info.h
@@ -23,7 +23,9 @@
 
   aura::Window* window;
 
-  // Index in MruWindowTracker to restore window stack.
+  // Index in MruWindowTracker to restore window stack. A larger index
+  // indicates a more recently used window. The index is also opposite of the
+  // window index in the MruWindowTracker at save time.
   base::Optional<int32_t> activation_index;
 
   // Virtual desk id.
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
index 853d64d..6e4f1a112 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -64,10 +64,11 @@
 
     /**
      * Hides the message banner.
+     * @param animate Whether to hide with an animation.
      * @param messageHidden The {@link Runnable} that will run once the message banner is hidden.
      */
-    void hide(Runnable messageHidden) {
-        mMediator.hide(messageHidden);
+    void hide(boolean animate, Runnable messageHidden) {
+        mMediator.hide(animate, messageHidden);
     }
 
     void setOnTouchRunnable(Runnable runnable) {
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
index 6b607d58..5fc9cc4b 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
@@ -116,15 +116,23 @@
 
     /**
      * Hides the message banner with an animation.
+     * @param animate Whether to hide with an animation.
      * @param messageHidden The {@link Runnable} that will run once the message banner is hidden.
      */
-    void hide(Runnable messageHidden) {
+    void hide(boolean animate, Runnable messageHidden) {
+        cancelAnyAnimations();
+
+        if (!animate) {
+            mModel.set(ALPHA, 0.f);
+            mModel.set(TRANSLATION_Y, -mMaxTranslationYSupplier.get());
+            mCurrentState = State.HIDDEN;
+        }
+
         if (mCurrentState == State.HIDDEN) {
             messageHidden.run();
             return;
         }
 
-        cancelAnyAnimations();
         startAnimation(true, -mMaxTranslationYSupplier.get(), false, messageHidden);
     }
 
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java
index 1566b29..b08b9cac 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java
@@ -110,7 +110,7 @@
 
         assertModelState(0, 0, 1, "fully shown.");
 
-        mMediator.hide(mHiddenRunnable);
+        mMediator.hide(true, mHiddenRunnable);
         verify(mHiddenRunnable, times(0)).run();
 
         shadowOf(getMainLooper()).idle();
@@ -120,6 +120,19 @@
     }
 
     @Test
+    public void testHideMessageNoAnimation() {
+        mMediator.show(mShownRunnable);
+
+        shadowOf(getMainLooper()).idle();
+
+        assertModelState(0, 0, 1, "fully shown.");
+
+        mMediator.hide(false, mHiddenRunnable);
+        assertModelState(0, -100, 0, "after hidden.");
+        verify(mHiddenRunnable, times(1)).run();
+    }
+
+    @Test
     public void testVerticalDismiss() {
         mMediator.show(mShownRunnable);
 
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
index 6d50135..1e5261ab 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
@@ -109,11 +109,7 @@
             mContainer.removeMessage(mView);
             if (hiddenCallback != null) hiddenCallback.run();
         };
-        if (animate) {
-            mMessageBanner.hide(hiddenRunnable);
-        } else {
-            hiddenRunnable.run();
-        }
+        mMessageBanner.hide(animate, hiddenRunnable);
     }
 
     /**
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java
index fe362e6..2d32db0 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java
@@ -5,6 +5,7 @@
 package org.chromium.components.messages;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -77,7 +78,7 @@
         message.hide(true, () -> {});
         // Let's pretend the animation ended, and the mediator called the callback as a result.
         final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        verify(messageBanner).hide(runnableCaptor.capture());
+        verify(messageBanner).hide(anyBoolean(), runnableCaptor.capture());
         runnableCaptor.getValue().run();
         Assert.assertEquals(
                 "Message container should not have any view after the message is hidden.", 0,
diff --git a/components/mirroring/mojom/session_observer.mojom b/components/mirroring/mojom/session_observer.mojom
index 3445aab9..8d32c461 100644
--- a/components/mirroring/mojom/session_observer.mojom
+++ b/components/mirroring/mojom/session_observer.mojom
@@ -24,8 +24,9 @@
   CAST_TRANSPORT_ERROR,           // Error occurred in cast transport.
 };
 
-// Observer interface for receiving notifications about significant lifecycle
-// events.
+// Observer interface for the browser process to receive notifications about
+// significant lifecycle events of a mirroring session. The notifications are
+// sent from the mirroring service running in a sandboxed utility process.
 interface SessionObserver {
   // Called when error occurred. The session will be stopped.
   OnError(SessionError error);
@@ -35,6 +36,10 @@
 
   // Called when the session stops.
   DidStop();
+
+  // Called when a message needs to be logged at INFO level by the MR logger.
+  LogInfoMessage(string message);
+
+  // Called when a message needs to be logged at ERROR level by the MR logger.
+  LogErrorMessage(string message);
 };
-
-
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index ec556d46..1959896 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -19,6 +19,7 @@
 #include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
@@ -469,6 +470,17 @@
   StopSession();
 }
 
+void Session::LogInfoMessage(const std::string& message) {
+  if (observer_) {
+    observer_->LogInfoMessage(message);
+  }
+}
+
+void Session::LogErrorMessage(const std::string& message) {
+  if (observer_) {
+    observer_->LogErrorMessage(message);
+  }
+}
 void Session::StopStreaming() {
   DVLOG(2) << __func__ << " state=" << state_;
   if (!cast_environment_)
@@ -828,6 +840,10 @@
                           base::CompareCase::SENSITIVE))) {
       QueryCapabilitiesForRemoting();
     }
+  } else {
+    LogInfoMessage(
+        base::StrCat({"Remoting is not supported on this receiver model: ",
+                      session_params_.receiver_model_name}));
   }
 
   if (initially_starting_session && observer_)
@@ -835,8 +851,7 @@
 }
 
 void Session::OnResponseParsingError(const std::string& error_message) {
-  // TODO(crbug.com/1117673): Add MR-internals logging:
-  //   VLOG(2) << "[REJECT] " << error_message;
+  LogErrorMessage(base::StrCat({"MessageDispatcher error: ", error_message}));
 }
 
 void Session::CreateAudioStream(
@@ -1007,11 +1022,14 @@
     return;
 
   if (!response.valid()) {
-    VLOG(1) << "Bad CAPABILITIES_RESPONSE. Remoting disabled.";
     if (response.error()) {
-      VLOG(1) << " error code=" << response.error()->code
-              << " description=" << response.error()->description
-              << " details=" << response.error()->details;
+      LogErrorMessage(base::StringPrintf(
+          "Remoting is not supported. Error code: %d, description: %s, "
+          "details: %s",
+          response.error()->code, response.error()->description.c_str(),
+          response.error()->details.c_str()));
+    } else {
+      LogErrorMessage("Remoting is not supported. Bad CAPABILITIES_RESPONSE.");
     }
     return;
   }
@@ -1026,8 +1044,10 @@
   }
 
   if (remoting_version > kSupportedRemotingVersion) {
-    VLOG(1) << "Unsupported remoting version (" << remoting_version << " > "
-            << kSupportedRemotingVersion << ')';
+    LogErrorMessage(
+        base::StringPrintf("Remoting is not supported. The receiver's remoting "
+                           "version (%d) is not supported by the sender (%d).",
+                           remoting_version, kSupportedRemotingVersion));
     return;
   }
 
diff --git a/components/mirroring/service/session.h b/components/mirroring/service/session.h
index 25c03dcc..1ff5dbf 100644
--- a/components/mirroring/service/session.h
+++ b/components/mirroring/service/session.h
@@ -54,8 +54,10 @@
 // Streaming, and the switching to/from media remoting. When constructed, it
 // does OFFER/ANSWER exchange with the mirroring receiver. Mirroring starts when
 // the exchange succeeds and stops when this class is destructed or error
-// occurs. |observer| will get notified when status changes. |outbound_channel|
-// is responsible for sending messages to the mirroring receiver through Cast
+// occurs. Specifically, a session is torn down when (1) a new session starts,
+// (2) the mirroring service note a disconnection.
+// |observer| will get notified when status changes. |outbound_channel| is
+// responsible for sending messages to the mirroring receiver through Cast
 // Channel. |inbound_channel| receives message sent from the mirroring receiver.
 class COMPONENT_EXPORT(MIRRORING_SERVICE) Session final
     : public RtpStreamClient,
@@ -135,6 +137,10 @@
   // Notify |observer_| that error occurred and close the session.
   void ReportError(mojom::SessionError error);
 
+  // Send logging messages to |observer_|.
+  void LogInfoMessage(const std::string& message);
+  void LogErrorMessage(const std::string& message);
+
   // Callback by Audio/VideoSender to indicate encoder status change.
   void OnEncoderStatusChange(media::cast::OperationalStatus status);
 
diff --git a/components/mirroring/service/session_unittest.cc b/components/mirroring/service/session_unittest.cc
index d5cdf53d8..f61003a8e 100644
--- a/components/mirroring/service/session_unittest.cc
+++ b/components/mirroring/service/session_unittest.cc
@@ -111,6 +111,8 @@
   MOCK_METHOD1(OnError, void(SessionError));
   MOCK_METHOD0(DidStart, void());
   MOCK_METHOD0(DidStop, void());
+  MOCK_METHOD1(LogInfoMessage, void(const std::string&));
+  MOCK_METHOD1(LogErrorMessage, void(const std::string&));
 
   MOCK_METHOD0(OnGetVideoCaptureHost, void());
   MOCK_METHOD0(OnGetNetworkContext, void());
diff --git a/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc b/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
index f4ff6b3..214edb8 100644
--- a/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
+++ b/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
@@ -9,6 +9,7 @@
 #include "components/optimization_guide/content/browser/test_optimization_guide_decider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h"
 
 namespace optimization_guide {
 
@@ -36,26 +37,13 @@
  protected:
   void Preprocess(const std::vector<TfLiteTensor*>& input_tensors,
                   const std::vector<float>& input) override {
-    // TODO(crbug/1176459): Use PopulateTensor instead.
-
-    float* tensor = reinterpret_cast<float*>(input_tensors[0]->data.raw);
-    size_t bytes = input.size() * sizeof(float);
-    memcpy(tensor, input.data(), bytes);
+    tflite::task::core::PopulateTensor<float>(input, input_tensors[0]);
   }
 
   std::vector<float> Postprocess(
       const std::vector<const TfLiteTensor*>& output_tensors) override {
-    // TODO(crbug/1176459): Use PopulateVector instead.
-
-    const TfLiteTensor* tensor = output_tensors[0];
-    const float* results = reinterpret_cast<const float*>(tensor->data.raw);
-    size_t num = tensor->bytes / sizeof(tensor->type);
-
     std::vector<float> data;
-    data.reserve(num);
-    for (size_t i = 0; i < num; i++) {
-      data.emplace_back(results[i]);
-    }
+    tflite::task::core::PopulateVector<float>(output_tensors[0], &data);
     return data;
   }
 };
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index 9815611..0308b46 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -31,6 +31,8 @@
     "page_load_metrics_embedder_base.h",
     "page_load_metrics_embedder_interface.h",
     "page_load_metrics_event.h",
+    "page_load_metrics_memory_tracker.cc",
+    "page_load_metrics_memory_tracker.h",
     "page_load_metrics_observer.cc",
     "page_load_metrics_observer.h",
     "page_load_metrics_observer_delegate.cc",
@@ -49,8 +51,11 @@
   deps = [
     "//build:chromeos_buildflags",
     "//components/data_reduction_proxy/core/browser",
+    "//components/keyed_service/content:content",
+    "//components/keyed_service/core:core",
     "//components/page_load_metrics/common",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
+    "//components/performance_manager:performance_manager",
     "//content/public/browser",
     "//content/public/common",
     "//net",
@@ -97,6 +102,7 @@
     "observers/page_load_metrics_observer_content_test_harness.h",
     "observers/prerender_page_load_metrics_observer_unittest.cc",
     "observers/use_counter_page_load_metrics_observer_unittest.cc",
+    "page_load_metrics_memory_tracker_unittest.cc",
     "page_load_metrics_util_unittest.cc",
     "resource_tracker_unittest.cc",
   ]
@@ -104,8 +110,10 @@
     ":browser",
     ":test_support",
     "//base/test:test_support",
+    "//components/keyed_service/content",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
     "//components/page_load_metrics/common:test_support",
+    "//components/performance_manager",
     "//components/ukm:test_support",
     "//components/ukm/content",
     "//content/public/browser",
diff --git a/components/page_load_metrics/browser/DEPS b/components/page_load_metrics/browser/DEPS
index b9b314d..28827d6 100644
--- a/components/page_load_metrics/browser/DEPS
+++ b/components/page_load_metrics/browser/DEPS
@@ -3,6 +3,9 @@
   "+content/public/browser",
   "+content/public/test",
   "+components/data_reduction_proxy/core/browser",
+  "+components/keyed_service/content",
+  "+components/keyed_service/core",
+  "+components/performance_manager/public",
   "+components/ukm",
   "+net",
   "+services/metrics",
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index 9ddb0af0..d1771de4 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
@@ -135,6 +136,9 @@
 }
 
 void MetricsWebContentsObserver::FrameDeleted(content::RenderFrameHost* rfh) {
+  if (auto* memory_tracker = GetMemoryTracker())
+    memory_tracker->OnFrameDeleted(rfh, this);
+
   if (committed_load_)
     committed_load_->FrameDeleted(rfh);
 }
@@ -981,6 +985,18 @@
     committed_load_->BroadcastEventToObservers(event);
 }
 
+void MetricsWebContentsObserver::OnV8MemoryChanged(
+    const std::vector<MemoryUpdate>& memory_updates) {
+  if (committed_load_)
+    committed_load_->OnV8MemoryChanged(memory_updates);
+}
+
+PageLoadMetricsMemoryTracker* MetricsWebContentsObserver::GetMemoryTracker()
+    const {
+  return embedder_interface_->GetMemoryTrackerForBrowserContext(
+      web_contents()->GetBrowserContext());
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(MetricsWebContentsObserver)
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.h b/components/page_load_metrics/browser/metrics_web_contents_observer.h
index ef84749..fdb2508 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.h
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.h
@@ -39,7 +39,9 @@
 
 namespace page_load_metrics {
 
+struct MemoryUpdate;
 class PageLoadMetricsEmbedderInterface;
+class PageLoadMetricsMemoryTracker;
 class PageLoadTracker;
 
 // MetricsWebContentsObserver tracks page loads and loading metrics
@@ -49,6 +51,7 @@
     : public content::WebContentsObserver,
       public content::WebContentsUserData<MetricsWebContentsObserver>,
       public content::RenderWidgetHost::InputEventObserver,
+      public base::SupportsWeakPtr<MetricsWebContentsObserver>,
       public mojom::PageLoadMetrics {
  public:
   // TestingObserver allows tests to observe MetricsWebContentsObserver state
@@ -175,13 +178,25 @@
   // WebContentsObserver::DidFinishNavigation methods.
   void BroadcastEventToObservers(PageLoadMetricsEvent event);
 
- private:
-  friend class content::WebContentsUserData<MetricsWebContentsObserver>;
+  // Called when V8 per-frame memory usage updates are available. Virtual for
+  // test classes to override.
+  virtual void OnV8MemoryChanged(
+      const std::vector<MemoryUpdate>& memory_updates);
 
+ protected:
+  // Protected rather than private so that derived test classes can call
+  // constructor.
   MetricsWebContentsObserver(
       content::WebContents* web_contents,
       std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface);
 
+ private:
+  friend class content::WebContentsUserData<MetricsWebContentsObserver>;
+
+  // Gets the memory tracker for the BrowserContext if it exists, or nullptr
+  // otherwise. The tracker measures per-frame memory usage by V8.
+  PageLoadMetricsMemoryTracker* GetMemoryTracker() const;
+
   void WillStartNavigationRequestImpl(
       content::NavigationHandle* navigation_handle);
 
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
index 08af036..cb48f01 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
@@ -864,6 +864,19 @@
   CheckNoErrorEvents();
 }
 
+TEST_F(MetricsWebContentsObserverTest, FlushBufferOnAppBackground) {
+  mojom::PageLoadTiming timing;
+  PopulatePageLoadTiming(&timing);
+  timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(100000);
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents(), GURL(kDefaultTestUrl));
+  SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
+
+  ASSERT_EQ(0, CountUpdatedTimingReported());
+  observer()->FlushMetricsOnAppEnterBackground();
+  ASSERT_EQ(1, CountUpdatedTimingReported());
+}
+
 TEST_F(MetricsWebContentsObserverTest,
        FirstInputDelayMissingFirstInputTimestamp) {
   mojom::PageLoadTiming timing;
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
index cf729f28..b37301e 100644
--- a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
@@ -26,8 +26,14 @@
 #include "third_party/blink/public/mojom/mobile_metrics/mobile_friendliness.mojom.h"
 #include "url/gurl.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
 namespace page_load_metrics {
 
+class PageLoadMetricsMemoryTracker;
+
 namespace {
 
 class TestPageLoadMetricsEmbedderInterface
@@ -57,6 +63,12 @@
 
   bool IsExtensionUrl(const GURL& url) override { return false; }
 
+  page_load_metrics::PageLoadMetricsMemoryTracker*
+  GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override {
+    return nullptr;
+  }
+
  private:
   PageLoadMetricsObserverTester* test_;
 
@@ -332,4 +344,15 @@
     register_callback_.Run(tracker);
 }
 
+void PageLoadMetricsObserverTester::SimulateMemoryUpdate(
+    content::RenderFrameHost* render_frame_host,
+    int64_t delta_bytes) {
+  DCHECK(render_frame_host);
+  if (delta_bytes != 0) {
+    std::vector<MemoryUpdate> update({MemoryUpdate(
+        render_frame_host->GetGlobalFrameRoutingId(), delta_bytes)});
+    metrics_web_contents_observer_->OnV8MemoryChanged(update);
+  }
+}
+
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
index 5bde3dd8..d5295f5 100644
--- a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
@@ -141,6 +141,10 @@
                              bool blocked_by_policy,
                              StorageType storage_type);
 
+  // Simulate a V8 per-frame memory update.
+  void SimulateMemoryUpdate(content::RenderFrameHost* render_frame_host,
+                            int64_t delta_bytes);
+
   void SimulateMobileFriendlinessUpdate(
       blink::MobileFriendliness& mobile_friendliness);
 
diff --git a/components/page_load_metrics/browser/page_load_metrics_embedder_interface.h b/components/page_load_metrics/browser/page_load_metrics_embedder_interface.h
index 419a2b3..0edef50 100644
--- a/components/page_load_metrics/browser/page_load_metrics_embedder_interface.h
+++ b/components/page_load_metrics/browser/page_load_metrics_embedder_interface.h
@@ -14,11 +14,13 @@
 }  // namespace base
 
 namespace content {
+class BrowserContext;
 class WebContents;
 }  // namespace content
 
 namespace page_load_metrics {
 
+class PageLoadMetricsMemoryTracker;
 class PageLoadTracker;
 
 // This class serves as a functional interface to various chrome// features.
@@ -31,6 +33,11 @@
   virtual std::unique_ptr<base::OneShotTimer> CreateTimer() = 0;
   virtual bool IsPrerender(content::WebContents* web_contents) = 0;
   virtual bool IsExtensionUrl(const GURL& url) = 0;
+
+  // Returns the PageLoadMetricsMemoryTracker for the given BrowserContext if
+  // tracking is enabled.
+  virtual PageLoadMetricsMemoryTracker* GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) = 0;
 };
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_memory_tracker.cc b/components/page_load_metrics/browser/page_load_metrics_memory_tracker.cc
new file mode 100644
index 0000000..4850195
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_memory_tracker.cc
@@ -0,0 +1,179 @@
+// Copyright 2021 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/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
+
+#include <map>
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace features {
+
+// Enables or disables per-frame memory monitoring.
+const base::Feature kV8PerFrameMemoryMonitoring{
+    "V8PerFrameMemoryMonitoring", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+
+namespace page_load_metrics {
+
+namespace {
+
+// WeakPtrs cannot be used as the key to a map without a custom comparator, so
+// we use a raw pointer for the map key and bundle the WeakPtr in a struct
+// to be used as the value.
+struct ObserverWeakPtrAndMemoryUpdates {
+  base::WeakPtr<MetricsWebContentsObserver> weak_ptr;
+  std::vector<MemoryUpdate> updates;
+  ObserverWeakPtrAndMemoryUpdates(
+      base::WeakPtr<MetricsWebContentsObserver> wk_ptr,
+      MemoryUpdate update)
+      : weak_ptr(wk_ptr) {
+    updates.emplace_back(update);
+  }
+};
+
+}  // namespace
+
+// Results of the V8PerAdFrameMemoryPollParamsStudy indicated that at the
+// ~99.8th percentile, collecting at 10-second or 60-second intervals
+// yields nearly equivalent results, as does using kBounded or kLazy mode.
+// As there is about 10% to 20% overhead total GC time, we chose the less
+// aggressive kLazy mode with a 60-second polling interval.
+// For further results please see crbug.com/1116087.
+PageLoadMetricsMemoryTracker::PageLoadMetricsMemoryTracker() {
+  if (base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring)) {
+    memory_request_ = std::make_unique<
+        performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>(
+        base::TimeDelta::FromSeconds(60),
+        performance_manager::v8_memory::V8DetailedMemoryRequest::
+            MeasurementMode::kLazy);
+    memory_request_->AddObserver(this);
+  }
+}
+
+PageLoadMetricsMemoryTracker::~PageLoadMetricsMemoryTracker() = default;
+
+void PageLoadMetricsMemoryTracker::Shutdown() {
+  if (memory_request_) {
+    memory_request_->RemoveObserver(this);
+    memory_request_.reset();
+  }
+}
+
+void PageLoadMetricsMemoryTracker::OnV8MemoryMeasurementAvailable(
+    performance_manager::RenderProcessHostId render_process_host_id,
+    const performance_manager::v8_memory::V8DetailedMemoryProcessData&
+        process_data,
+    const performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq::
+        FrameDataMap& frame_data) {
+  std::map<MetricsWebContentsObserver*, ObserverWeakPtrAndMemoryUpdates>
+      memory_update_map;
+
+  // Iterate through frames with available measurements.
+  for (const auto& map_pair : frame_data) {
+    content::GlobalFrameRoutingId frame_routing_id = map_pair.first;
+    content::RenderFrameHost* rfh =
+        content::RenderFrameHost::FromID(frame_routing_id);
+
+    // We lose a small amount of data due to a RenderFrameHost
+    // sometimes no longer being alive by the time that a report is received.
+    // UMA suggests we miss about 0.078% of updates on desktop and about 0.11%
+    // on mobile (as measured 10/30/2020).
+    // See crbug.com/1116087.
+    if (!rfh)
+      continue;
+
+    int64_t delta_bytes =
+        UpdateMemoryUsageAndGetDelta(rfh, map_pair.second.v8_bytes_used());
+
+    // Only send updates that are nontrivial.
+    if (delta_bytes == 0)
+      continue;
+
+    // Note that at this point, we are guaranteed that the frame is alive, and
+    // frames cannot exist without an owning WebContents.
+    content::WebContents* web_contents =
+        content::WebContents::FromRenderFrameHost(rfh);
+    MetricsWebContentsObserver* observer =
+        MetricsWebContentsObserver::FromWebContents(web_contents);
+
+    if (!observer)
+      continue;
+
+    auto emplace_pair = memory_update_map.emplace(std::make_pair(
+        observer,
+        ObserverWeakPtrAndMemoryUpdates(
+            observer->AsWeakPtr(),
+            MemoryUpdate(rfh->GetGlobalFrameRoutingId(), delta_bytes))));
+
+    if (!emplace_pair.second) {
+      emplace_pair.first->second.updates.emplace_back(
+          MemoryUpdate(rfh->GetGlobalFrameRoutingId(), delta_bytes));
+    }
+  }
+
+  // Dispatch memory updates to each observer. Note that we store references to
+  // MetricsWebContentsObservers as weakptrs. This is done to ensure that if a
+  // WebContents was torn down synchronously as the result of a memory update
+  // in a different WebContents, we would not have a dangling pointer.
+  for (const auto& map_pair : memory_update_map) {
+    MetricsWebContentsObserver* observer = map_pair.second.weak_ptr.get();
+
+    if (!observer)
+      continue;
+
+    observer->OnV8MemoryChanged(map_pair.second.updates);
+  }
+}
+
+void PageLoadMetricsMemoryTracker::OnFrameDeleted(
+    content::RenderFrameHost* render_frame_host,
+    MetricsWebContentsObserver* observer) {
+  DCHECK(render_frame_host);
+  DCHECK(observer);
+
+  auto it = per_frame_memory_usage_map_.find(render_frame_host->GetRoutingID());
+
+  if (it == per_frame_memory_usage_map_.end())
+    return;
+
+  // The routing id for |render_frame_host| has been found in our usage map.
+  // We assume that the renderer has released the frame and that its
+  // contents will be picked up by the next GC. So for all intents and
+  // purposes, the memory is freed at this point, and we remove the entry from
+  // our usage map and notify observers of the delta.
+  int64_t delta_bytes = -it->second;
+  per_frame_memory_usage_map_.erase(it);
+
+  // Only send updates that are nontrivial.
+  if (delta_bytes == 0)
+    return;
+
+  std::vector<MemoryUpdate> update({MemoryUpdate(
+      render_frame_host->GetGlobalFrameRoutingId(), delta_bytes)});
+  observer->OnV8MemoryChanged(update);
+}
+
+int64_t PageLoadMetricsMemoryTracker::UpdateMemoryUsageAndGetDelta(
+    content::RenderFrameHost* render_frame_host,
+    uint64_t current_bytes_used) {
+  DCHECK(render_frame_host);
+
+  int64_t delta_bytes = current_bytes_used;
+  int routing_id = render_frame_host->GetRoutingID();
+  auto it = per_frame_memory_usage_map_.find(routing_id);
+
+  if (it != per_frame_memory_usage_map_.end()) {
+    delta_bytes -= it->second;
+    it->second = current_bytes_used;
+  } else {
+    per_frame_memory_usage_map_[routing_id] = current_bytes_used;
+  }
+
+  return delta_bytes;
+}
+
+}  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_memory_tracker.h b/components/page_load_metrics/browser/page_load_metrics_memory_tracker.h
new file mode 100644
index 0000000..c6c2283
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_memory_tracker.h
@@ -0,0 +1,68 @@
+// Copyright 2021 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_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_MEMORY_TRACKER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_MEMORY_TRACKER_H_
+
+#include "base/containers/flat_map.h"
+#include "base/feature_list.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace features {
+
+extern const base::Feature kV8PerFrameMemoryMonitoring;
+
+}  // namespace features
+
+namespace page_load_metrics {
+
+// PageLoadMetricsMemoryTracker tracks per-frame memory usage by V8 and
+// forwards an individual per-frame measurement to the
+// MetricsWebContentsObserver associated with the WebContents containing that
+// frame.
+class PageLoadMetricsMemoryTracker
+    : public KeyedService,
+      public performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq {
+ public:
+  PageLoadMetricsMemoryTracker();
+  ~PageLoadMetricsMemoryTracker() override;
+  PageLoadMetricsMemoryTracker(const PageLoadMetricsMemoryTracker&) = delete;
+  PageLoadMetricsMemoryTracker& operator=(const PageLoadMetricsMemoryTracker&) =
+      delete;
+
+  // KeyedService:
+  void Shutdown() override;
+
+  // performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq:
+  void OnV8MemoryMeasurementAvailable(
+      performance_manager::RenderProcessHostId render_process_host_id,
+      const performance_manager::v8_memory::V8DetailedMemoryProcessData&
+          process_data,
+      const performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq::
+          FrameDataMap& frame_data) override;
+
+  // Removes the entry for a deleted frame from `per_frame_memory_usage_map_`.
+  void OnFrameDeleted(content::RenderFrameHost* render_frame_host,
+                      MetricsWebContentsObserver* observer);
+
+ private:
+  int64_t UpdateMemoryUsageAndGetDelta(
+      content::RenderFrameHost* render_frame_host,
+      uint64_t current_bytes_used);
+
+  // Tracks the most recent per-frame measurements by frame routing id.
+  base::flat_map<int, uint64_t> per_frame_memory_usage_map_;
+
+  // Allows receipt of per-frame V8 memory measurements once instantiated
+  // and PageLoadMetricsMemoryTracker is added as an observer.
+  std::unique_ptr<performance_manager::v8_memory::V8DetailedMemoryRequestAnySeq>
+      memory_request_;
+};
+
+}  // namespace page_load_metrics
+
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_MEMORY_TRACKER_H_
diff --git a/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc b/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc
new file mode 100644
index 0000000..d69f9f2
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc
@@ -0,0 +1,296 @@
+// Copyright 2021 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/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
+
+#include "base/memory/singleton.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
+#include "components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h"
+#include "components/performance_manager/public/render_process_host_id.h"
+#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_client.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using V8DetailedMemoryExecutionContextData =
+    performance_manager::v8_memory::V8DetailedMemoryExecutionContextData;
+using FrameDataMap = base::flat_map<content::GlobalFrameRoutingId,
+                                    V8DetailedMemoryExecutionContextData>;
+
+const char kMainUrl[] = "https://main.com/";
+const char kSubUrl[] = "https://foo.com/";
+const char kOtherSubUrl[] = "https://bar.com/";
+
+namespace page_load_metrics {
+
+namespace {
+
+class TestPageLoadMetricsEmbedder
+    : public page_load_metrics::PageLoadMetricsEmbedderBase {
+ public:
+  explicit TestPageLoadMetricsEmbedder(content::WebContents* web_contents)
+      : PageLoadMetricsEmbedderBase(web_contents) {}
+  TestPageLoadMetricsEmbedder(const TestPageLoadMetricsEmbedder&) = delete;
+  TestPageLoadMetricsEmbedder& operator=(const TestPageLoadMetricsEmbedder&) =
+      delete;
+  ~TestPageLoadMetricsEmbedder() override = default;
+
+  // page_load_metrics::PageLoadMetricsEmbedderBase:
+  bool IsNewTabPageUrl(const GURL& url) override { return false; }
+  bool IsPrerender(content::WebContents* web_contents) override {
+    return false;
+  }
+  bool IsExtensionUrl(const GURL& url) override { return false; }
+
+  page_load_metrics::PageLoadMetricsMemoryTracker*
+  GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override {
+    if (!base::FeatureList::IsEnabled(features::kV8PerFrameMemoryMonitoring))
+      return nullptr;
+
+    return &memory_tracker_;
+  }
+
+ protected:
+  // page_load_metrics::PageLoadMetricsEmbedderBase:
+  void RegisterEmbedderObservers(
+      page_load_metrics::PageLoadTracker* tracker) override {}
+  bool IsPrerendering() const override { return false; }
+
+ private:
+  page_load_metrics::PageLoadMetricsMemoryTracker memory_tracker_;
+};
+
+class TestMestricsWebContentsObserver : public MetricsWebContentsObserver {
+ public:
+  TestMestricsWebContentsObserver(
+      content::WebContents* web_contents,
+      std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
+      : MetricsWebContentsObserver(web_contents,
+                                   std::move(embedder_interface)) {}
+
+  int num_updates_received() const { return num_updates_received_; }
+
+  const base::flat_map<int, int64_t>& last_memory_deltas_received() const {
+    return last_memory_deltas_received_;
+  }
+
+  void OnV8MemoryChanged(
+      const std::vector<MemoryUpdate>& memory_updates) override {
+    for (const auto& update : memory_updates) {
+      num_updates_received_++;
+
+      int routing_id = update.routing_id.frame_routing_id;
+      auto it = last_memory_deltas_received_.find(routing_id);
+      if (it == last_memory_deltas_received_.end())
+        last_memory_deltas_received_[routing_id] = update.delta_bytes;
+      else
+        it->second = update.delta_bytes;
+    }
+  }
+
+ private:
+  base::flat_map<int, int64_t> last_memory_deltas_received_;
+  int num_updates_received_ = 0;
+};
+
+class PageLoadMetricsMemoryTrackerTest
+    : public content::RenderViewHostTestHarness {
+ public:
+  PageLoadMetricsMemoryTrackerTest() = default;
+  ~PageLoadMetricsMemoryTrackerTest() override = default;
+  PageLoadMetricsMemoryTrackerTest(const PageLoadMetricsMemoryTrackerTest&) =
+      delete;
+  PageLoadMetricsMemoryTrackerTest& operator=(
+      const PageLoadMetricsMemoryTrackerTest&) = delete;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kV8PerFrameMemoryMonitoring);
+
+    content::RenderViewHostTestHarness::SetUp();
+    original_browser_client_ =
+        content::SetBrowserClientForTesting(&browser_client_);
+
+    auto embedder_interface =
+        std::make_unique<TestPageLoadMetricsEmbedder>(web_contents());
+    embedder_interface_ = embedder_interface.get();
+    observer_ = new TestMestricsWebContentsObserver(
+        web_contents(), std::move(embedder_interface));
+    web_contents()->SetUserData(TestMestricsWebContentsObserver::UserDataKey(),
+                                base::WrapUnique(observer_));
+
+    tracker_ = embedder_interface_->GetMemoryTrackerForBrowserContext(
+        browser_context());
+  }
+
+  void TearDown() override {
+    content::SetBrowserClientForTesting(original_browser_client_);
+    tracker_->Shutdown();
+    content::RenderViewHostTestHarness::TearDown();
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  content::RenderFrameHost* NavigateFrame(const std::string& url,
+                                          content::RenderFrameHost* frame) {
+    auto navigation_simulator =
+        content::NavigationSimulator::CreateRendererInitiated(GURL(url), frame);
+    navigation_simulator->Commit();
+    return navigation_simulator->GetFinalRenderFrameHost();
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  content::RenderFrameHost* NavigateMainFrame(const std::string& url) {
+    return NavigateFrame(url, web_contents()->GetMainFrame());
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  content::RenderFrameHost* CreateAndNavigateSubFrame(
+      const std::string& url,
+      content::RenderFrameHost* parent) {
+    content::RenderFrameHost* subframe =
+        content::RenderFrameHostTester::For(parent)->AppendChild("frame_name");
+    auto navigation_simulator =
+        content::NavigationSimulator::CreateRendererInitiated(GURL(url),
+                                                              subframe);
+    navigation_simulator->Commit();
+
+    return navigation_simulator->GetFinalRenderFrameHost();
+  }
+
+  void SimulateMemoryMeasurementUpdate(
+      content::RenderFrameHost* render_frame_host,
+      uint64_t bytes) {
+    if (!render_frame_host || !render_frame_host->GetProcess())
+      return;
+
+    content::GlobalFrameRoutingId global_routing_id =
+        render_frame_host->GetGlobalFrameRoutingId();
+    int process_id = render_frame_host->GetProcess()->GetID();
+
+    performance_manager::RenderProcessHostId pm_process_id =
+        static_cast<performance_manager::RenderProcessHostId>(process_id);
+    performance_manager::v8_memory::V8DetailedMemoryProcessData process_data;
+    V8DetailedMemoryExecutionContextData frame_data;
+    frame_data.set_v8_bytes_used(bytes);
+
+    FrameDataMap frame_map;
+    frame_map[global_routing_id] = frame_data;
+
+    tracker_->OnV8MemoryMeasurementAvailable(pm_process_id, process_data,
+                                             frame_map);
+  }
+
+  int num_updates_received() const { return observer_->num_updates_received(); }
+
+  const base::flat_map<int, int64_t>& last_memory_deltas_received() const {
+    return observer_->last_memory_deltas_received();
+  }
+
+ protected:
+  PageLoadMetricsMemoryTracker* tracker_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  TestMestricsWebContentsObserver* observer_;
+  TestPageLoadMetricsEmbedder* embedder_interface_;
+  PageLoadMetricsTestContentBrowserClient browser_client_;
+  content::ContentBrowserClient* original_browser_client_ = nullptr;
+};
+
+}  // namespace
+
+TEST_F(PageLoadMetricsMemoryTrackerTest,
+       InitialUpdatesOnly_CorrectDeltasReceived) {
+  content::RenderFrameHost* main_frame = NavigateMainFrame(kMainUrl);
+  int main_id = main_frame->GetRoutingID();
+  content::RenderFrameHost* sub_frame1 =
+      CreateAndNavigateSubFrame(kSubUrl, main_frame);
+  int sub_frame1_id = sub_frame1->GetRoutingID();
+
+  // Create a nested subframe with the same origin as its parent.
+  content::RenderFrameHost* sub_frame2 =
+      CreateAndNavigateSubFrame(kOtherSubUrl, sub_frame1);
+  int sub_frame2_id = sub_frame2->GetRoutingID();
+
+  SimulateMemoryMeasurementUpdate(main_frame, 100 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame1, 200 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame2, 300 * 1024);
+
+  auto deltas_received = last_memory_deltas_received();
+  EXPECT_EQ(3, num_updates_received());
+  ASSERT_EQ(3UL, deltas_received.size());
+
+  EXPECT_TRUE(deltas_received.find(main_id) != deltas_received.end());
+  EXPECT_EQ(100L, deltas_received[main_id] / 1024);
+  EXPECT_TRUE(deltas_received.find(sub_frame1_id) != deltas_received.end());
+  EXPECT_EQ(200L, deltas_received[sub_frame1_id] / 1024);
+  EXPECT_TRUE(deltas_received.find(sub_frame2_id) != deltas_received.end());
+  EXPECT_EQ(300L, deltas_received[sub_frame2_id] / 1024);
+}
+
+TEST_F(PageLoadMetricsMemoryTrackerTest, SecondUpdates_CorrectDeltasReceived) {
+  content::RenderFrameHost* main_frame = NavigateMainFrame(kMainUrl);
+  int main_id = main_frame->GetRoutingID();
+  content::RenderFrameHost* sub_frame1 =
+      CreateAndNavigateSubFrame(kSubUrl, main_frame);
+  int sub_frame1_id = sub_frame1->GetRoutingID();
+
+  // Create a nested subframe with the same origin as its parent.
+  content::RenderFrameHost* sub_frame2 =
+      CreateAndNavigateSubFrame(kOtherSubUrl, sub_frame1);
+  int sub_frame2_id = sub_frame2->GetRoutingID();
+
+  SimulateMemoryMeasurementUpdate(main_frame, 100 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame1, 200 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame2, 300 * 1024);
+
+  // Simulate second round of updates.
+  SimulateMemoryMeasurementUpdate(main_frame, 50 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame1, 300 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame2, 100 * 1024);
+
+  auto deltas_received = last_memory_deltas_received();
+  EXPECT_EQ(6, num_updates_received());
+  ASSERT_EQ(3UL, deltas_received.size());
+
+  EXPECT_TRUE(deltas_received.find(main_id) != deltas_received.end());
+  EXPECT_EQ(-50L, deltas_received[main_id] / 1024);
+  EXPECT_TRUE(deltas_received.find(sub_frame1_id) != deltas_received.end());
+  EXPECT_EQ(100L, deltas_received[sub_frame1_id] / 1024);
+  EXPECT_TRUE(deltas_received.find(sub_frame2_id) != deltas_received.end());
+  EXPECT_EQ(-200L, deltas_received[sub_frame2_id] / 1024);
+}
+
+TEST_F(PageLoadMetricsMemoryTrackerTest, FrameDeleted_CorrectDeltasReceived) {
+  content::RenderFrameHost* main_frame = NavigateMainFrame(kMainUrl);
+  int main_id = main_frame->GetRoutingID();
+  content::RenderFrameHost* sub_frame =
+      CreateAndNavigateSubFrame(kSubUrl, main_frame);
+  int sub_frame_id = sub_frame->GetRoutingID();
+
+  SimulateMemoryMeasurementUpdate(main_frame, 100 * 1024);
+  SimulateMemoryMeasurementUpdate(sub_frame, 200 * 1024);
+
+  // Delete |sub_frame| and refresh the usage map. An update should have been
+  // received that will make the usage corresponding to |sub_frame| zero.
+  content::RenderFrameHostTester::For(sub_frame)->Detach();
+
+  auto deltas_received = last_memory_deltas_received();
+  EXPECT_EQ(3, num_updates_received());
+  ASSERT_EQ(2UL, deltas_received.size());
+
+  EXPECT_TRUE(deltas_received.find(main_id) != deltas_received.end());
+  EXPECT_EQ(100L, deltas_received[main_id] / 1024);
+  EXPECT_TRUE(deltas_received.find(sub_frame_id) != deltas_received.end());
+  EXPECT_EQ(-200L, deltas_received[sub_frame_id] / 1024);
+}
+
+}  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_observer.cc b/components/page_load_metrics/browser/page_load_metrics_observer.cc
index 148a3b4..c2b2fe38 100644
--- a/components/page_load_metrics/browser/page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_observer.cc
@@ -8,6 +8,9 @@
 
 namespace page_load_metrics {
 
+MemoryUpdate::MemoryUpdate(content::GlobalFrameRoutingId id, int64_t delta)
+    : routing_id(id), delta_bytes(delta) {}
+
 ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
     const url::Origin& origin_of_final_url,
     const net::IPEndPoint& remote_endpoint,
diff --git a/components/page_load_metrics/browser/page_load_metrics_observer.h b/components/page_load_metrics/browser/page_load_metrics_observer.h
index 3e30bea..2c9a5ff 100644
--- a/components/page_load_metrics/browser/page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/page_load_metrics_observer.h
@@ -30,6 +30,13 @@
 
 namespace page_load_metrics {
 
+// Struct for storing per-frame memory update data.
+struct MemoryUpdate {
+  content::GlobalFrameRoutingId routing_id;
+  int64_t delta_bytes;
+  MemoryUpdate(content::GlobalFrameRoutingId id, int64_t delta);
+};
+
 // Storage types reported to page load metrics observers on storage
 // accesses.
 enum class StorageType {
@@ -544,6 +551,12 @@
   // portal.
   virtual void DidActivatePortal(base::TimeTicks activation_time) {}
 
+  // Called when V8 per-frame memory usage updates are available. Each
+  // MemoryUpdate consists of a GlobalFrameRoutingId and a nonzero int64_t
+  // change in bytes used.
+  virtual void OnV8MemoryChanged(
+      const std::vector<MemoryUpdate>& memory_updates) {}
+
  private:
   PageLoadMetricsObserverDelegate* delegate_ = nullptr;
 };
diff --git a/components/page_load_metrics/browser/page_load_tracker.cc b/components/page_load_metrics/browser/page_load_tracker.cc
index 231e4ae..3f2d908 100644
--- a/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/components/page_load_metrics/browser/page_load_tracker.cc
@@ -15,6 +15,7 @@
 #include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
+#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/common/page_load_timing.h"
@@ -490,6 +491,8 @@
 }
 
 void PageLoadTracker::FlushMetricsOnAppEnterBackground() {
+  metrics_update_dispatcher()->FlushPendingTimingUpdates();
+
   if (!app_entered_background_) {
     RecordAppBackgroundPageLoadCompleted(false);
     app_entered_background_ = true;
@@ -988,4 +991,10 @@
   }
 }
 
+void PageLoadTracker::OnV8MemoryChanged(
+    const std::vector<MemoryUpdate>& memory_updates) {
+  for (const auto& observer : observers_)
+    observer->OnV8MemoryChanged(memory_updates);
+}
+
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_tracker.h b/components/page_load_metrics/browser/page_load_tracker.h
index eb043f4..c48fcf0 100644
--- a/components/page_load_metrics/browser/page_load_tracker.h
+++ b/components/page_load_metrics/browser/page_load_tracker.h
@@ -40,6 +40,7 @@
 
 namespace page_load_metrics {
 
+struct MemoryUpdate;
 class PageLoadMetricsEmbedderInterface;
 
 namespace internal {
@@ -374,6 +375,9 @@
   // portal.
   void DidActivatePortal(base::TimeTicks activation_time);
 
+  // Called when V8 per-frame memory usage updates are available.
+  void OnV8MemoryChanged(const std::vector<MemoryUpdate>& memory_updates);
+
  private:
   // This function converts a TimeTicks value taken in the browser process
   // to navigation_start_ if:
diff --git a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
index 4dfdd9ff6..26c6571 100644
--- a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
+++ b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
@@ -179,4 +179,10 @@
   return false;
 }
 
+PageLoadMetricsMemoryTracker*
+TestMetricsWebContentsObserverEmbedder::GetMemoryTrackerForBrowserContext(
+    content::BrowserContext* browser_context) {
+  return nullptr;
+}
+
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h
index 6579c5c..ee48b812 100644
--- a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h
+++ b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h
@@ -16,6 +16,8 @@
 
 namespace page_load_metrics {
 
+class PageLoadMetricsMemoryTracker;
+
 class TestMetricsWebContentsObserverEmbedder
     : public PageLoadMetricsEmbedderInterface,
       public test::WeakMockTimerProvider {
@@ -29,6 +31,8 @@
   std::unique_ptr<base::OneShotTimer> CreateTimer() override;
   bool IsPrerender(content::WebContents* web_contents) override;
   bool IsExtensionUrl(const GURL& url) override;
+  PageLoadMetricsMemoryTracker* GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override;
 
   void set_is_ntp(bool is_ntp) { is_ntp_ = is_ntp; }
 
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 882ad03d..d92e0378 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -110,11 +110,11 @@
 // dynamic form change.
 const base::Feature kReparseServerPredictionsFollowingFormChange = {
     "ReparseServerPredictionsFollowingFormChange",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables considering secondary server field predictions during form parsing.
 const base::Feature kSecondaryServerFieldPredictions = {
-    "SecondaryServerFieldPredictions", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SecondaryServerFieldPredictions", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables syncing of compromised credentials.
 const base::Feature kSyncingCompromisedCredentials = {
diff --git a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
index eed1019..d2708f4 100644
--- a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
+++ b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
@@ -166,6 +166,7 @@
           // Use MOCK_TIME so that ExpectQueryAndDelayReply can be used.
           base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
   GetGraphFeaturesHelper().EnableExecutionContextRegistry();
+  GetGraphFeaturesHelper().EnableV8ContextTracker();
 }
 
 V8MemoryPerformanceManagerTestHarness::
diff --git a/components/performance_manager/v8_memory/web_memory_aggregator.cc b/components/performance_manager/v8_memory/web_memory_aggregator.cc
index f4064ff..fc8cfa49 100644
--- a/components/performance_manager/v8_memory/web_memory_aggregator.cc
+++ b/components/performance_manager/v8_memory/web_memory_aggregator.cc
@@ -28,10 +28,13 @@
 // TODO(joenotcharles): If we ever need to aggregate different data for each
 // aggregation point, turn this into an interface and add a subclass for each
 // type of data to aggregate.
-class WebMemoryAggregator::AggregationPointVisitor {
+class AggregationPointVisitor {
  public:
-  AggregationPointVisitor(const FrameNode* aggregation_start_node,
-                          const url::Origin& requesting_origin);
+  // The given |main_origin| is the origin of the main web page, which is the
+  // same as the origin of the top-level frames.
+  AggregationPointVisitor(const url::Origin& requesting_origin,
+                          const ProcessNode* requesting_process_node,
+                          const url::Origin& main_origin);
 
   ~AggregationPointVisitor();
 
@@ -42,39 +45,81 @@
   mojom::WebMemoryMeasurementPtr TakeAggregationResult();
 
   // Called on first visiting |frame_node| in a depth-first traversal.
-  // |aggregation_type| specificies how to treat the node in the aggregation.
-  void OnFrameEntered(const FrameNode* frame_node,
-                      NodeAggregationType aggregation_type);
+  void OnFrameEntered(const FrameNode* frame_node);
 
   // Called after visiting |frame_node| and all its children in a depth-first
   // traversal.
   void OnFrameExited(const FrameNode* frame_node);
 
   // Called on first visiting |worker_node| in a depth-first traversal.
-  // |aggregation_type| specificies how to treat the node in the aggregation.
-  void OnWorkerEntered(const WorkerNode* worker_node,
-                       NodeAggregationType aggregation_type);
+  void OnWorkerEntered(const WorkerNode* worker_node);
 
   // Called after visiting |worker_node| and all its children in a depth-first
   // traversal.
   void OnWorkerExited(const WorkerNode* worker_node);
 
+  // Called at the start of the depth-first traversal to set up the common
+  // root node for all frame trees.
+  void OnRootEntered();
+
+  // Called at the end of the traversal.
+  void OnRootExited();
+
  private:
-  const FrameNode* aggregation_start_node_;
+  struct Enclosing {
+    url::Origin origin;
+    mojom::WebMemoryBreakdownEntry* aggregation_point;
+  };
   const url::Origin requesting_origin_;
+  const ProcessNode* requesting_process_node_;
+  const url::Origin main_origin_;
   mojom::WebMemoryMeasurementPtr aggregation_result_ =
       mojom::WebMemoryMeasurement::New();
-  base::stack<mojom::WebMemoryBreakdownEntry*> enclosing_aggregation_points_;
+  mojom::WebMemoryBreakdownEntryPtr root_aggregation_point_;
+  base::stack<Enclosing> enclosing_;
 };
 
 namespace {
 
 using AttributionScope = mojom::WebMemoryAttribution::Scope;
 
-// Returns true if |page_node| has an opener that should be followed by the
-// aggregation algorithm.
-bool ShouldFollowOpenerLink(const PageNode* page_node) {
-  return page_node->GetOpenedType() == PageNode::OpenedType::kPopup;
+// The various ways a node can be treated during the aggregation.
+enum class NodeAggregationType {
+  // Node is same-origin to |requesting_node| and its iframe attributes are
+  // visible;
+  // will be a new aggregation point with a scope depending on the node type
+  // (eg. "Window" or "DedicatedWorker").
+  kSameOriginAggregationPoint,
+  // Node is same-origin to |requesting_node| but its iframe attributes are not
+  // visible;
+  // will be a new aggregation point with a scope depending on the node type
+  // (eg. "Window" or "DedicatedWorker").
+  kSameOriginAggregationPointWithHiddenAttributes,
+  // Node is cross-origin with |requesting_node| but its parent is not; will
+  // be a new aggregation point with scope "cross-origin-aggregated".
+  kCrossOriginAggregationPoint,
+  // Node is cross-origin with |requesting_node| and so is its parent; will
+  // be aggregated into its parent's aggregation point.
+  kCrossOriginAggregated,
+};
+
+NodeAggregationType GetNodeAggregationType(const url::Origin& requesting_origin,
+                                           const url::Origin& enclosing_origin,
+                                           const url::Origin& node_origin) {
+  bool same_origin_node = requesting_origin.IsSameOriginWith(node_origin);
+  bool same_origin_parent =
+      requesting_origin.IsSameOriginWith(enclosing_origin);
+
+  if (same_origin_node) {
+    return same_origin_parent
+               ? NodeAggregationType::kSameOriginAggregationPoint
+               : NodeAggregationType::
+                     kSameOriginAggregationPointWithHiddenAttributes;
+  } else {
+    return same_origin_parent
+               ? NodeAggregationType::kCrossOriginAggregationPoint
+               : NodeAggregationType::kCrossOriginAggregated;
+  }
 }
 
 // Returns |frame_node|'s origin based on its current url.
@@ -97,20 +142,6 @@
 }
 #endif
 
-// Returns the parent of |frame_node|, the opener if it has no parent, or
-// nullptr if it has neither.
-const FrameNode* GetParentOrOpener(const FrameNode* frame_node) {
-  // Only the main frame of a page should have an opener. So first check if
-  // there's a parent and, if not, check if there's an opener.
-  if (auto* parent = frame_node->GetParentFrameNode())
-    return parent;
-  auto* page_node = frame_node->GetPageNode();
-  DCHECK(page_node);
-  if (ShouldFollowOpenerLink(page_node))
-    return page_node->GetOpenerFrameNode();
-  return nullptr;
-}
-
 // Returns a mutable pointer to the WebMemoryAttribution structure in the given
 // |breakdown|.
 mojom::WebMemoryAttribution* GetAttributionFromBreakdown(
@@ -162,83 +193,117 @@
   aggregation_point->memory->bytes += bytes_used;
 }
 
+const FrameNode* GetTopFrame(const FrameNode* frame) {
+  DCHECK(frame);
+  // Follow the parent to find the top-most frame.
+  auto* current = frame;
+  while (auto* parent = current->GetParentFrameNode()) {
+    current = parent;
+  }
+
+  DCHECK(current);
+  // Make sure we didn't break out of the browsing context group.
+  DCHECK_EQ(current->GetBrowsingInstanceId(), frame->GetBrowsingInstanceId());
+  return current;
+}
+
+// Returns the process node of the main frame that is in the same browsing
+// context group as the given frame.
+const ProcessNode* GetMainProcess(const FrameNode* frame) {
+  // COOP guarantees that the top-most frame of the current frame tree
+  // and the main frame of the page have the same origin and thus have
+  // the same process node.
+  return GetTopFrame(frame)->GetProcessNode();
+}
+
 }  // anonymous namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 // AggregationPointVisitor
 
-WebMemoryAggregator::AggregationPointVisitor::AggregationPointVisitor(
-    const FrameNode* aggregation_start_node,
-    const url::Origin& requesting_origin)
-    : aggregation_start_node_(aggregation_start_node),
-      requesting_origin_(requesting_origin) {}
+AggregationPointVisitor::AggregationPointVisitor(
+    const url::Origin& requesting_origin,
+    const ProcessNode* requesting_process_node,
+    const url::Origin& main_origin)
+    : requesting_origin_(requesting_origin),
+      requesting_process_node_(requesting_process_node),
+      main_origin_(main_origin) {}
 
-WebMemoryAggregator::AggregationPointVisitor::~AggregationPointVisitor() =
-    default;
+AggregationPointVisitor::~AggregationPointVisitor() {
+  DCHECK(enclosing_.empty());
+}
 
 mojom::WebMemoryMeasurementPtr
-WebMemoryAggregator::AggregationPointVisitor::TakeAggregationResult() {
+AggregationPointVisitor::TakeAggregationResult() {
   DCHECK(aggregation_result_);
   auto result = std::move(aggregation_result_);
   aggregation_result_ = nullptr;
   return result;
 }
 
-void WebMemoryAggregator::AggregationPointVisitor::OnFrameEntered(
-    const FrameNode* frame_node,
-    NodeAggregationType aggregation_type) {
+void AggregationPointVisitor::OnRootEntered() {
+  DCHECK(enclosing_.empty());
+  root_aggregation_point_ = mojom::WebMemoryBreakdownEntry::New();
+  root_aggregation_point_->attribution.emplace_back(
+      mojom::WebMemoryAttribution::New());
+  enclosing_.push(Enclosing{main_origin_, root_aggregation_point_.get()});
+}
+
+void AggregationPointVisitor::OnRootExited() {
+  if (root_aggregation_point_->memory) {
+    aggregation_result_->breakdown.push_back(
+        std::move(root_aggregation_point_));
+  }
+  enclosing_.pop();
+  DCHECK(enclosing_.empty());
+}
+
+void AggregationPointVisitor::OnFrameEntered(const FrameNode* frame_node) {
+  DCHECK(!enclosing_.empty());
   DCHECK(frame_node);
-  DCHECK_EQ(enclosing_aggregation_points_.empty(),
-            frame_node == aggregation_start_node_);
+  url::Origin node_origin = GetOrigin(frame_node);
+  NodeAggregationType aggregation_type = GetNodeAggregationType(
+      requesting_origin_, enclosing_.top().origin, node_origin);
   mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
   switch (aggregation_type) {
-    case NodeAggregationType::kInvisible:
-      NOTREACHED();
-      return;
-
     case NodeAggregationType::kSameOriginAggregationPoint:
-      // Create a new aggregation point with window scope. Since this node is
-      // same-origin to the start node, the start node can view its current
-      // url.
-      aggregation_point = CreateBreakdownEntry(AttributionScope::kWindow,
-                                               frame_node->GetURL().spec(),
-                                               aggregation_result_.get());
-      if (frame_node->IsMainFrame() || frame_node == aggregation_start_node_) {
-        // There should be no id or src attribute since there is no visible
-        // parent to take them from. Do nothing.
-      } else if (GetSameOriginParentOrOpener(frame_node, requesting_origin_)) {
-        // The parent or opener is also same-origin so the start node can view
-        // its attributes. Add the id and src recorded for the node in
-        // V8ContextTracker to the new breakdown entry.
-        SetBreakdownAttributionFromFrame(frame_node, aggregation_point);
-      } else {
-        // Some grandparent node is the most recent aggregation point whose
-        // attributes are visible to the start node, and
-        // |enclosing_aggregation_point| includes those attributes. Copy the
-        // id and src attributes from there.
-        CopyBreakdownAttribution(enclosing_aggregation_points_.top(),
-                                 aggregation_point);
-      }
+      aggregation_point = WebMemoryAggregator::CreateBreakdownEntry(
+          AttributionScope::kWindow, frame_node->GetURL().spec(),
+          aggregation_result_.get());
+      WebMemoryAggregator::SetBreakdownAttributionFromFrame(frame_node,
+                                                            aggregation_point);
       break;
 
+    case NodeAggregationType::kSameOriginAggregationPointWithHiddenAttributes:
+      aggregation_point = WebMemoryAggregator::CreateBreakdownEntry(
+          AttributionScope::kWindow, frame_node->GetURL().spec(),
+          aggregation_result_.get());
+      // Some grandparent node is the most recent aggregation point whose
+      // attributes are visible to the start node, and
+      // |enclosing_aggregation_point| includes those attributes. Copy the
+      // id and src attributes from there.
+      WebMemoryAggregator::CopyBreakdownAttribution(
+          enclosing_.top().aggregation_point, aggregation_point);
+      break;
     case NodeAggregationType::kCrossOriginAggregationPoint:
       // Create a new aggregation point with cross-origin-aggregated scope.
       // Since this node is NOT same-origin to the start node, the start node
       // CANNOT view its current url.
-      aggregation_point =
-          CreateBreakdownEntry(AttributionScope::kCrossOriginAggregated,
-                               base::nullopt, aggregation_result_.get());
+      aggregation_point = WebMemoryAggregator::CreateBreakdownEntry(
+          AttributionScope::kCrossOriginAggregated, base::nullopt,
+          aggregation_result_.get());
       // This is cross-origin but not being aggregated into another
       // aggregation point, so its parent or opener must be same-origin to the
       // start node, which can therefore view its attributes. Add the id and
       // src recorded for the node in V8ContextTracker to the new breakdown
       // entry.
-      SetBreakdownAttributionFromFrame(frame_node, aggregation_point);
+      WebMemoryAggregator::SetBreakdownAttributionFromFrame(frame_node,
+                                                            aggregation_point);
       break;
 
     case NodeAggregationType::kCrossOriginAggregated:
       // Update the enclosing aggregation point in-place.
-      aggregation_point = enclosing_aggregation_points_.top();
+      aggregation_point = enclosing_.top().aggregation_point;
       break;
   }
 
@@ -246,46 +311,65 @@
   DCHECK(aggregation_point);
   AddMemoryBytes(aggregation_point,
                  V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node),
-                 frame_node->GetProcessNode() ==
-                     aggregation_start_node_->GetProcessNode());
+                 frame_node->GetProcessNode() == requesting_process_node_);
 
-  enclosing_aggregation_points_.push(aggregation_point);
+  enclosing_.push(Enclosing{node_origin, aggregation_point});
 }
 
-void WebMemoryAggregator::AggregationPointVisitor::OnFrameExited(
-    const FrameNode* frame_node) {
-  DCHECK(!enclosing_aggregation_points_.empty());
-  enclosing_aggregation_points_.pop();
+void AggregationPointVisitor::OnFrameExited(const FrameNode* frame_node) {
+  enclosing_.pop();
+  DCHECK(!enclosing_.empty());
 }
 
-void WebMemoryAggregator::AggregationPointVisitor::OnWorkerEntered(
-    const WorkerNode* worker_node,
-    NodeAggregationType aggregation_type) {
+void AggregationPointVisitor::OnWorkerEntered(const WorkerNode* worker_node) {
+  DCHECK(!enclosing_.empty());
   DCHECK(worker_node);
-
-  // Aggregation starts from a frame node, so the enclosing aggregation point
-  // is guaranteed to exist.
-  DCHECK(!enclosing_aggregation_points_.empty());
+  // TODO(crbug.com/1169168): Support service and shared workers.
+  DCHECK_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
+  // A dedicated worker is guaranteed to have the same origin as its parent,
+  // which means that a dedicated worker cannot be a cross-origin aggregation
+  // point.
+  // TODO(crbug.com/1169178): The URL of a worker node is currently not
+  // available without PlzDedicatedWorker, which is disabled by default.
+  // Until then we use the origin of the parent.
+  url::Origin node_origin = enclosing_.top().origin;
+#if DCHECK_IS_ON()
+  auto client_frames = worker_node->GetClientFrames();
+  DCHECK(std::all_of(client_frames.begin(), client_frames.end(),
+                     [node_origin](const FrameNode* client) {
+                       return node_origin.IsSameOriginWith(GetOrigin(client));
+                     }));
+  auto client_workers = worker_node->GetClientWorkers();
+  DCHECK(std::all_of(client_workers.begin(), client_workers.end(),
+                     [node_origin](const WorkerNode* client) {
+                       // TODO(crbug.com/1169178): Remove the is_empty guard
+                       // once worker worker URLs are available.
+                       return client->GetURL().is_empty() ||
+                              node_origin.IsSameOriginWith(GetOrigin(client));
+                     }));
+#endif
+  NodeAggregationType aggregation_type = GetNodeAggregationType(
+      requesting_origin_, enclosing_.top().origin, node_origin);
 
   mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
   switch (aggregation_type) {
     case NodeAggregationType::kSameOriginAggregationPoint:
+    case NodeAggregationType::kSameOriginAggregationPointWithHiddenAttributes:
       // Create a new aggregation point with window scope. Since this node is
       // same-origin to the start node, the start node can view its current
       // url.
-      aggregation_point = CreateBreakdownEntry(
+      aggregation_point = WebMemoryAggregator::CreateBreakdownEntry(
           AttributionScopeFromWorkerType(worker_node->GetWorkerType()),
           worker_node->GetURL().spec(), aggregation_result_.get());
-      CopyBreakdownAttribution(enclosing_aggregation_points_.top(),
-                               aggregation_point);
+      WebMemoryAggregator::CopyBreakdownAttribution(
+          enclosing_.top().aggregation_point, aggregation_point);
       break;
 
     case NodeAggregationType::kCrossOriginAggregated:
       // Update the enclosing aggregation point in-place.
-      aggregation_point = enclosing_aggregation_points_.top();
+      aggregation_point = enclosing_.top().aggregation_point;
       break;
 
-    case NodeAggregationType::kInvisible:
     case NodeAggregationType::kCrossOriginAggregationPoint:
       NOTREACHED();
       return;
@@ -296,16 +380,14 @@
   AddMemoryBytes(
       aggregation_point,
       V8DetailedMemoryExecutionContextData::ForWorkerNode(worker_node),
-      worker_node->GetProcessNode() ==
-          aggregation_start_node_->GetProcessNode());
+      worker_node->GetProcessNode() == requesting_process_node_);
 
-  enclosing_aggregation_points_.push(aggregation_point);
+  enclosing_.push(Enclosing{node_origin, aggregation_point});
 }
 
-void WebMemoryAggregator::AggregationPointVisitor::OnWorkerExited(
-    const WorkerNode* worker_node) {
-  DCHECK(!enclosing_aggregation_points_.empty());
-  enclosing_aggregation_points_.pop();
+void AggregationPointVisitor::OnWorkerExited(const WorkerNode* worker_node) {
+  enclosing_.pop();
+  DCHECK(!enclosing_.empty());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -313,23 +395,46 @@
 
 WebMemoryAggregator::WebMemoryAggregator(const FrameNode* requesting_node)
     : requesting_origin_(GetOrigin(requesting_node)),
-      aggregation_start_node_(FindAggregationStartNode(requesting_node)) {
-  DCHECK(aggregation_start_node_);
-}
+      requesting_process_node_(requesting_node->GetProcessNode()),
+      main_process_node_(GetMainProcess(requesting_node)),
+      browsing_instance_id_(requesting_node->GetBrowsingInstanceId()) {}
 
 WebMemoryAggregator::~WebMemoryAggregator() = default;
 
 mojom::WebMemoryMeasurementPtr
 WebMemoryAggregator::AggregateMeasureMemoryResult() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  AggregationPointVisitor ap_visitor(aggregation_start_node_,
-                                     requesting_origin_);
-  VisitFrame(&ap_visitor, aggregation_start_node_);
 
+  std::vector<const FrameNode*> top_frames;
+  main_process_node_->VisitFrameNodes(base::BindRepeating(
+      [](std::vector<const FrameNode*>* top_frames,
+         int32_t browsing_instance_id, const FrameNode* node) {
+        if (node->GetBrowsingInstanceId() == browsing_instance_id &&
+            !node->GetParentFrameNode() && !GetOrigin(node).opaque()) {
+          top_frames->push_back(node);
+        }
+        return true;
+      },
+      &top_frames, browsing_instance_id_));
+
+  CHECK(!top_frames.empty());
+  url::Origin main_origin = GetOrigin(top_frames[0]);
+  DCHECK(std::all_of(top_frames.begin(), top_frames.end(),
+                     [&main_origin](const FrameNode* node) {
+                       return GetOrigin(node).IsSameOriginWith(main_origin);
+                     }));
+
+  AggregationPointVisitor ap_visitor(requesting_origin_,
+                                     requesting_process_node_, main_origin);
+  ap_visitor.OnRootEntered();
+  for (const FrameNode* node : top_frames) {
+    VisitFrame(&ap_visitor, node);
+  }
+  ap_visitor.OnRootExited();
   mojom::WebMemoryMeasurementPtr aggregation_result =
       ap_visitor.TakeAggregationResult();
-  auto* process_data = V8DetailedMemoryProcessData::ForProcessNode(
-      aggregation_start_node_->GetProcessNode());
+  auto* process_data =
+      V8DetailedMemoryProcessData::ForProcessNode(requesting_process_node_);
   if (process_data) {
     aggregation_result->detached_memory = mojom::WebMemoryUsage::New();
     aggregation_result->detached_memory->bytes =
@@ -338,130 +443,21 @@
     aggregation_result->shared_memory->bytes =
         process_data->shared_v8_bytes_used();
   }
-
   return aggregation_result;
 }
 
-WebMemoryAggregator::NodeAggregationType
-WebMemoryAggregator::FindNodeAggregationType(const FrameNode* frame_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-#if DCHECK_IS_ON()
-  auto* node = frame_node;
-  while (node && node != aggregation_start_node_) {
-    node = GetParentOrOpener(node);
-  }
-  // Should have broken out of the loop by reaching the start node, not nullptr.
-  DCHECK_EQ(node, aggregation_start_node_);
-#endif
-
-  // If |frame_node| is in a different browsing context group from |start_node|
-  // it should be invisible.
-  if (frame_node->GetBrowsingInstanceId() !=
-      aggregation_start_node_->GetBrowsingInstanceId()) {
-    return NodeAggregationType::kInvisible;
-  }
-
-  auto frame_origin = GetOrigin(frame_node);
-
-  // If |frame_node| is same-origin to |start_node|, it's an aggregation point.
-  // (This trivially includes the |start_node| itself.)
-  if (requesting_origin_.IsSameOriginWith(frame_origin))
-    return NodeAggregationType::kSameOriginAggregationPoint;
-  DCHECK_NE(frame_node, aggregation_start_node_);
-
-  // If |frame_node| is cross-origin from |start_node|, but is a direct child of
-  // a same-origin node, its existence is visible to |start_node| so it's an
-  // aggregation point. But its current url will be hidden from |start_node|.
-  const FrameNode* parent_node = frame_node->GetParentFrameNode();
-
-  if (!parent_node) {
-    // A cross-origin window opened via window.open gets its own browsing
-    // context group due to COOP. However, while the window is being loaded it
-    // belongs to the old browsing context group. In that case the origin is
-    // opaque.
-    DCHECK(frame_origin.opaque());
-    return NodeAggregationType::kInvisible;
-  }
-
-  auto parent_origin = GetOrigin(parent_node);
-  if (requesting_origin_.IsSameOriginWith(parent_origin)) {
-    return NodeAggregationType::kCrossOriginAggregationPoint;
-  }
-
-  // Otherwise |frame_node|'s memory should be aggregated into the last
-  // aggregation point.
-  return NodeAggregationType::kCrossOriginAggregated;
-}
-
-WebMemoryAggregator::NodeAggregationType
-WebMemoryAggregator::FindNodeAggregationType(const WorkerNode* worker_node,
-                                             NodeAggregationType parent_type) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // TODO(crbug.com/1169168): Support service and shared workers.
-  DCHECK_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
-  // A dedicated worker is guaranteed to have the same origin as its parent,
-  // which means that a dedicated worker cannot be a cross-origin aggregation
-  // point.
-#if DCHECK_IS_ON()
-  // TODO(crbug.com/1169178): The URL of a worker node is currently not
-  // available without PlzDedicatedWorker, which is disabled by default. Remove
-  // this guard once the URL is properly propagated to PM.
-  if (!worker_node->GetURL().is_empty()) {
-    auto worker_origin = GetOrigin(worker_node);
-    auto client_frames = worker_node->GetClientFrames();
-    DCHECK(std::all_of(client_frames.begin(), client_frames.end(),
-                       [worker_origin](const FrameNode* client) {
-                         return worker_origin.IsSameOriginWith(
-                             GetOrigin(client));
-                       }));
-    auto client_workers = worker_node->GetClientWorkers();
-    DCHECK(std::all_of(client_workers.begin(), client_workers.end(),
-                       [worker_origin](const WorkerNode* client) {
-                         return worker_origin.IsSameOriginWith(
-                             GetOrigin(client));
-                       }));
-  }
-#endif
-  switch (parent_type) {
-    case NodeAggregationType::kCrossOriginAggregationPoint:
-      return NodeAggregationType::kCrossOriginAggregated;
-    case NodeAggregationType::kCrossOriginAggregated:
-    case NodeAggregationType::kSameOriginAggregationPoint:
-      return parent_type;
-    case NodeAggregationType::kInvisible:
-      // Visitation stops at an invisible node and does not enter its children.
-      NOTREACHED();
-      return NodeAggregationType::kInvisible;
-  }
-}
-
 bool WebMemoryAggregator::VisitFrame(AggregationPointVisitor* ap_visitor,
                                      const FrameNode* frame_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(frame_node);
-
-  // An aggregation point is a node in the graph that holds a memory breakdown
-  // covering itself and any descendant nodes that are aggregated into the same
-  // breakdown. It is represented directly by the WebMemoryBreakdownEntry
-  // object the describes the breakdown since there is no extra information to
-  // store about the aggregation point.
-  auto aggregation_type = FindNodeAggregationType(frame_node);
-  if (aggregation_type == NodeAggregationType::kInvisible) {
-    // Ignore this node, continue iterating its siblings.
+  if (frame_node->GetBrowsingInstanceId() != browsing_instance_id_) {
+    // Ignore frames from other browsing contexts.
     return true;
   }
+  ap_visitor->OnFrameEntered(frame_node);
 
-  ap_visitor->OnFrameEntered(frame_node, aggregation_type);
-
-  // Recurse into children and opened pages. Unretained is safe because the
-  // Visit* functions are synchronous.
-  frame_node->VisitOpenedPageNodes(
-      base::BindRepeating(&WebMemoryAggregator::VisitOpenedPage,
-                          base::Unretained(this), ap_visitor));
   frame_node->VisitChildDedicatedWorkers(base::BindRepeating(
-      &WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor,
-      aggregation_type));
+      &WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor));
   frame_node->VisitChildFrameNodes(base::BindRepeating(
       &WebMemoryAggregator::VisitFrame, base::Unretained(this), ap_visitor));
 
@@ -470,85 +466,22 @@
   return true;
 }
 
-bool WebMemoryAggregator::VisitWorker(
-    AggregationPointVisitor* ap_visitor,
-    NodeAggregationType parent_aggregation_type,
-    const WorkerNode* worker_node) {
+bool WebMemoryAggregator::VisitWorker(AggregationPointVisitor* ap_visitor,
+                                      const WorkerNode* worker_node) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(crbug.com/1169168): Support service and shared workers.
   DCHECK_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  NodeAggregationType aggregation_type =
-      FindNodeAggregationType(worker_node, parent_aggregation_type);
-  if (aggregation_type == NodeAggregationType::kInvisible) {
-    // Ignore this node, continue iterating its siblings.
-    return true;
-  }
-
-  ap_visitor->OnWorkerEntered(worker_node, aggregation_type);
+  ap_visitor->OnWorkerEntered(worker_node);
 
   worker_node->VisitChildDedicatedWorkers(base::BindRepeating(
-      &WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor,
-      aggregation_type));
+      &WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor));
 
   ap_visitor->OnWorkerExited(worker_node);
 
   return true;
 }
 
-bool WebMemoryAggregator::VisitOpenedPage(AggregationPointVisitor* ap_visitor,
-                                          const PageNode* page_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (ShouldFollowOpenerLink(page_node)) {
-    // Visit only the "current" main frame instead of all of the main frames
-    // (non-current ones are either about to die, or represent an ongoing
-    // navigation).
-    return VisitFrame(ap_visitor, page_node->GetMainFrameNode());
-  }
-  return true;
-}
-
-// static
-const FrameNode* WebMemoryAggregator::GetSameOriginParentOrOpener(
-    const FrameNode* frame_node,
-    const url::Origin& origin) {
-  if (auto* parent_or_opener = GetParentOrOpener(frame_node)) {
-    if (origin.IsSameOriginWith(GetOrigin(parent_or_opener)))
-      return parent_or_opener;
-  }
-  return nullptr;
-}
-
-// static
-const FrameNode* WebMemoryAggregator::FindAggregationStartNode(
-    const FrameNode* requesting_node) {
-  DCHECK(requesting_node);
-  auto requesting_origin = GetOrigin(requesting_node);
-  DCHECK(!requesting_origin.opaque());
-
-  // Follow parent and opener links to find the most general same-site node to
-  // start the aggregation traversal from.
-  const FrameNode* start_node = nullptr;
-  for (auto* parent_or_opener = requesting_node; parent_or_opener;
-       parent_or_opener =
-           GetSameOriginParentOrOpener(parent_or_opener, requesting_origin)) {
-    // Only consider nodes in the same process as potential start nodes.
-    // (https://github.com/WICG/performance-measure-memory/issues/20).
-    if (parent_or_opener->GetProcessNode() ==
-        requesting_node->GetProcessNode()) {
-      start_node = parent_or_opener;
-    }
-  }
-
-  DCHECK(start_node);
-  DCHECK(requesting_origin.IsSameOriginWith(GetOrigin(start_node)));
-
-  // Make sure we didn't break out of the browsing context group.
-  DCHECK_EQ(start_node->GetBrowsingInstanceId(),
-            requesting_node->GetBrowsingInstanceId());
-  return start_node;
-}
-
 // static
 mojom::WebMemoryBreakdownEntry* WebMemoryAggregator::CreateBreakdownEntry(
     AttributionScope scope,
diff --git a/components/performance_manager/v8_memory/web_memory_aggregator.h b/components/performance_manager/v8_memory/web_memory_aggregator.h
index 63981d40..1c93376 100644
--- a/components/performance_manager/v8_memory/web_memory_aggregator.h
+++ b/components/performance_manager/v8_memory/web_memory_aggregator.h
@@ -15,11 +15,13 @@
 namespace performance_manager {
 
 class FrameNode;
-class PageNode;
+class ProcessNode;
 class WorkerNode;
 
 namespace v8_memory {
 
+class AggregationPointVisitor;
+
 // Traverses the graph of execution contexts to find the results of the last
 // memory measurement and aggregates them according to the rules defined in the
 // performance.measureUserAgentSpecificMemory spec.
@@ -32,55 +34,20 @@
   //
   // The aggregation is performed by calling AggregateMemoryResult. The graph
   // traversal will not start directly from |requesting_node|, but from the
-  // highest node in the frame tree that is visible to it as found by
-  // FindAggregationStartNode. (This allows a same-origin subframe to request
-  // memory for the whole page it's embedded in.)
+  // top frame nodes.
   explicit WebMemoryAggregator(const FrameNode* requesting_node);
   ~WebMemoryAggregator();
 
   WebMemoryAggregator(const WebMemoryAggregator& other) = delete;
   WebMemoryAggregator& operator=(const WebMemoryAggregator& other) = delete;
 
-  // Returns the origin of |requesting_node|.
-  const url::Origin& requesting_origin() const { return requesting_origin_; }
-
   // Performs the aggregation.
   mojom::WebMemoryMeasurementPtr AggregateMeasureMemoryResult();
 
  private:
+  friend class AggregationPointVisitor;
   friend class WebMemoryAggregatorTest;
 
-  class AggregationPointVisitor;
-
-  // The various ways a node can be treated during the aggregation.
-  enum class NodeAggregationType {
-    // Node is same-origin to |requesting_node|; will be a new aggregation
-    // point with a scope depending on the node type (eg. "Window" or
-    // "DedicatedWorker").
-    kSameOriginAggregationPoint,
-    // Node is cross-origin with |requesting_node| but its parent is not; will
-    // be a new aggregation point with scope
-    // "cross-origin-aggregated".
-    kCrossOriginAggregationPoint,
-    // Node is cross-origin with |requesting_node| and so is its parent; will
-    // be aggregated into its parent's aggregation point.
-    kCrossOriginAggregated,
-    // Node is in a different browsing context group; will not be added to the
-    // aggregation.
-    kInvisible,
-  };
-
-  // Returns the way that |frame_node| should be treated during the
-  // aggregation.  |aggregation_start_node_| must be reachable from
-  // |frame_node| by following parent/child or opener links. This will always
-  // be true if |frame_node| comes from a call to VisitFrame.
-  NodeAggregationType FindNodeAggregationType(const FrameNode* frame_node);
-  // Returns the aggregation type of a dedicated worker node based on its
-  // parent's aggregation type.
-  NodeAggregationType FindNodeAggregationType(
-      const WorkerNode* worker_node,
-      NodeAggregationType parent_aggregation_type);
-
   // FrameNodeVisitor that recursively adds |frame_node| and its children to
   // the aggregation using |ap_visitor|. Always returns true to continue
   // traversal.
@@ -88,34 +55,11 @@
                   const FrameNode* frame_node);
 
   // WorkerNodeVisitor that recursively adds |worker_node| and its children to
-  // the aggregation using |ap_visitor|. |enclosing_aggregation_type| is the
-  // type of the aggregation point that |worker_node|'s parent is in. Always
-  // returns true to continue traversal.
+  // the aggregation using |ap_visitor|. Always returns true to continue
+  // traversal.
   bool VisitWorker(AggregationPointVisitor* ap_visitor,
-                   NodeAggregationType enclosing_aggregation_type,
                    const WorkerNode* worker_node);
 
-  // PageNodeVisitor that recursively adds |page_node|'s main frames and their
-  // children to the aggregation using |ap_visitor|. Always returns true to
-  // continue traversal.
-  bool VisitOpenedPage(AggregationPointVisitor* ap_visitor,
-                       const PageNode* page_node);
-
-  // Static private methods are implementation details, but can be accessed from
-  // friend classes for testing.
-
-  // Returns |frame_node|'s parent or opener if the parent or opener is
-  // same-origin with |origin|, nullptr otherwise.
-  static const FrameNode* GetSameOriginParentOrOpener(
-      const FrameNode* frame_node,
-      const url::Origin& origin);
-
-  // Walks back the chain of parents and openers from |requesting_node| to find
-  // the farthest ancestor that should be visible to it (all intermediate nodes
-  // in the chain are same-origin).
-  static const FrameNode* FindAggregationStartNode(
-      const FrameNode* requesting_node);
-
   // Creates a new breakdown entry with the given |scope| and |url|, and adds it
   // to the list in |measurement|. Returns a pointer to the newly created entry.
   static mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
@@ -134,13 +78,14 @@
       const mojom::WebMemoryBreakdownEntry* from,
       mojom::WebMemoryBreakdownEntry* to);
 
-  // The origin of |requesting_node|. Cached so it doesn't have to be
-  // recalculated in each call to VisitFrame.
+  // The origin of the node that requests memory measurement.
   const url::Origin requesting_origin_;
-
-  // The node that the graph traversal should start from, found from
-  // |requesting_node| using FindAggregationStartNode.
-  const FrameNode* aggregation_start_node_;
+  // The process node of the requesting frame.
+  const ProcessNode* const requesting_process_node_;
+  // The process node of the main frame.
+  const ProcessNode* const main_process_node_;
+  // The browsing instance id of the requesting frame.
+  const int32_t browsing_instance_id_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 };
diff --git a/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc b/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
index 6ce9fb3..cf917b4 100644
--- a/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
+++ b/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
@@ -98,36 +98,8 @@
 
 class WebMemoryAggregatorTest : public WebMemoryTestHarness {
  protected:
-  using NodeAggregationType = WebMemoryAggregator::NodeAggregationType;
-
   // Allow individual test subclasses to access private members of
   // WebMemoryAggregator.
-
-  static NodeAggregationType FindNodeAggregationType(
-      WebMemoryAggregator* aggregator,
-      const FrameNode* frame_node) {
-    return aggregator->FindNodeAggregationType(frame_node);
-  }
-
-  static NodeAggregationType FindNodeAggregationType(
-      WebMemoryAggregator* aggregator,
-      const WorkerNode* worker_node,
-      NodeAggregationType parent_aggregation_type) {
-    return aggregator->FindNodeAggregationType(worker_node,
-                                               parent_aggregation_type);
-  }
-
-  static const FrameNode* GetSameOriginParentOrOpener(
-      const FrameNode* frame_node,
-      const url::Origin& origin) {
-    return WebMemoryAggregator::GetSameOriginParentOrOpener(frame_node, origin);
-  }
-
-  static const FrameNode* FindAggregationStartNode(
-      const FrameNode* requesting_node) {
-    return WebMemoryAggregator::FindAggregationStartNode(requesting_node);
-  }
-
   static mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
       mojom::WebMemoryAttribution::Scope scope,
       base::Optional<std::string> url,
@@ -210,7 +182,6 @@
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
   });
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
@@ -219,22 +190,11 @@
 TEST_F(WebMemoryAggregatorTest, AggregateSingleSiteMultiFrame) {
   // Example 2 from http://wicg.github.io/performance-measure-memory/#examples
   FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10});
-  FrameNodeImpl* child_frame =
-      AddFrameNode("https://example.com/iframe.html", Bytes{5}, main_frame,
-                   "example-id", "redirect.html?target=iframe.html");
+  AddFrameNode("https://example.com/iframe.html", Bytes{5}, main_frame,
+               "example-id", "redirect.html?target=iframe.html");
 
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
 
-  // Test the relationships of each node in the graph.
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
-      main_frame);
-
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
@@ -262,44 +222,17 @@
   FrameNodeImpl* child_frame =
       AddFrameNode("https://foo.com/iframe1", Bytes{5}, main_frame,
                    "example-id", "https://foo.com/iframe1");
-  FrameNodeImpl* grandchild1 =
-      AddFrameNode("https://foo.com/iframe2", Bytes{2}, child_frame,
-                   "example-id2", "https://foo.com/iframe2");
-  FrameNodeImpl* grandchild2 =
-      AddFrameNode("https://bar.com/iframe2", Bytes{3}, child_frame,
-                   "example-id3", "https://bar.com/iframe2");
-  // TODO(crbug.com/1085129): In the spec this is a worker, but they're not
-  // supported yet.
-  FrameNodeImpl* grandchild3 =
-      AddFrameNode("https://foo.com/worker.js", Bytes{4}, child_frame);
+  AddFrameNode("https://foo.com/iframe2", Bytes{2}, child_frame, "example-id2",
+               "https://foo.com/iframe2");
+  AddFrameNode("https://bar.com/iframe2", Bytes{3}, child_frame, "example-id3",
+               "https://bar.com/iframe2");
 
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
+  WorkerNodeImpl* worker =
+      AddWorkerNode(WorkerNode::WorkerType::kDedicated,
+                    "https://foo.com/worker.js", Bytes{4}, child_frame);
+
   WebMemoryAggregator aggregator(main_frame);
 
-  // Test the relationships of each node in the graph.
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
-            NodeAggregationType::kCrossOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
-      main_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild1),
-            NodeAggregationType::kCrossOriginAggregated);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(grandchild1, aggregator.requesting_origin()),
-      nullptr);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild2),
-            NodeAggregationType::kCrossOriginAggregated);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(grandchild2, aggregator.requesting_origin()),
-      nullptr);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild3),
-            NodeAggregationType::kCrossOriginAggregated);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(grandchild3, aggregator.requesting_origin()),
-      nullptr);
-
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
@@ -309,6 +242,7 @@
   });
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  worker->RemoveClientFrame(child_frame);
 }
 
 TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) {
@@ -343,81 +277,26 @@
   FrameNodeImpl* subframe4 =
       AddFrameNode("https://foo.com/iframe2", Bytes{2}, subframe3,
                    "example-id4", "https://foo.com/iframe2");
-  FrameNodeImpl* subframe5 =
-      AddFrameNode("https://example.com/iframe2", Bytes{1}, subframe4,
-                   "example-id5", "https://example.com/iframe2");
-  FrameNodeImpl* subframe6 =
-      AddFrameNode("https://example.com/iframe3", Bytes{6}, subframe3,
-                   "example-id6", "https://example.com/iframe3");
+  AddFrameNode("https://example.com/iframe2", Bytes{1}, subframe4,
+               "example-id5", "https://example.com/iframe2");
+  AddFrameNode("https://example.com/iframe3", Bytes{6}, subframe3,
+               "example-id6", "https://example.com/iframe3");
 
   // To test aggregation all the frames above are in the same process, even
   // though in production frames with different origins will be in different
   // processes whenever possible. Frames in a different process from the
   // requesting frame should all have 0 bytes reported.
-  FrameNodeImpl* cross_process_frame =
-      AddCrossProcessFrameNode("https://example.com/cross_process", Bytes{100},
-                               subframe3, "cross-process-id1");
-  FrameNodeImpl* cross_process_frame2 =
-      AddCrossProcessFrameNode("https://foo.com/cross_process", Bytes{200},
-                               subframe3, "cross-process-id2");
+  AddCrossProcessFrameNode("https://example.com/cross_process", Bytes{100},
+                           subframe3, "cross-process-id1");
+  AddCrossProcessFrameNode("https://foo.com/cross_process", Bytes{200},
+                           subframe3, "cross-process-id2");
 
   // A frame without a memory measurement (eg. a frame that's added to the frame
   // tree during the measurement) should not have a memory entry in the result.
-  FrameNodeImpl* empty_frame =
-      AddFrameNode("https://example.com/empty_frame", base::nullopt, subframe3);
+  AddFrameNode("https://example.com/empty_frame", base::nullopt, subframe3);
 
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
 
-  // Test the relationships of each node in the graph.
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe),
-            NodeAggregationType::kCrossOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe, aggregator.requesting_origin()),
-      main_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe2),
-            NodeAggregationType::kCrossOriginAggregated);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe2, aggregator.requesting_origin()),
-      nullptr);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe3),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe3, aggregator.requesting_origin()),
-      nullptr);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe4),
-            NodeAggregationType::kCrossOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe4, aggregator.requesting_origin()),
-      subframe3);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe5),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe5, aggregator.requesting_origin()),
-      nullptr);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe6),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(subframe6, aggregator.requesting_origin()),
-      subframe3);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, empty_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(empty_frame, aggregator.requesting_origin()),
-      subframe3);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_process_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(GetSameOriginParentOrOpener(cross_process_frame,
-                                        aggregator.requesting_origin()),
-            subframe3);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_process_frame2),
-            NodeAggregationType::kCrossOriginAggregationPoint);
-  EXPECT_EQ(GetSameOriginParentOrOpener(cross_process_frame2,
-                                        aggregator.requesting_origin()),
-            subframe3);
-
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
@@ -457,7 +336,6 @@
                               "https://example.com/"),
       ExpectedMemoryBreakdown(20, AttributionScope::kWindow, "about:blank"),
   });
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
@@ -475,86 +353,20 @@
       ExpectedMemoryBreakdown(50, AttributionScope::kCrossOriginAggregated,
                               base::nullopt),
   });
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
 }
 
-TEST_F(WebMemoryAggregatorTest, FindAggregationStartNode) {
-  FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10});
-  FrameNodeImpl* cross_site_child = AddFrameNode(
-      "https://foo.com/iframe.html", Bytes{5}, main_frame, "example-id", "");
-  FrameNodeImpl* same_site_child =
-      AddFrameNode("https://foo.com/iframe2.html", Bytes{4}, cross_site_child,
-                   "example-id2", "");
-
-  // FindAggregationStartNode should return the parent foo.com frame for either
-  // foo.com child. It should not return the main frame since it's cross-site
-  // from the requesting frames.
-  EXPECT_EQ(FindAggregationStartNode(cross_site_child), cross_site_child);
-  EXPECT_EQ(FindAggregationStartNode(same_site_child), cross_site_child);
-
-  // When aggregation starts at |cross_site_child| it should not include any
-  // memory from the main frame.
-  WebMemoryAggregator aggregator(cross_site_child);
-  auto expected_result = CreateExpectedMemoryMeasurement({
-      ExpectedMemoryBreakdown(5, AttributionScope::kWindow,
-                              "https://foo.com/iframe.html"),
-      ExpectedMemoryBreakdown(4, AttributionScope::kWindow,
-                              "https://foo.com/iframe2.html", "example-id2",
-                              ""),
-  });
-  auto result = aggregator.AggregateMeasureMemoryResult();
-  EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
-
-  // When the main frame requests a measurement of the same tree it should
-  // aggregate the children, which are cross-site from it.
-  EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
-  auto main_frame_expected_result = CreateExpectedMemoryMeasurement({
-      ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
-                              "https://example.com/"),
-      ExpectedMemoryBreakdown(9, AttributionScope::kCrossOriginAggregated,
-                              base::nullopt, "example-id", ""),
-  });
-  WebMemoryAggregator main_frame_aggregator(main_frame);
-  auto main_frame_result = main_frame_aggregator.AggregateMeasureMemoryResult();
-  EXPECT_EQ(MeasurementToJSON(main_frame_result),
-            MeasurementToJSON(main_frame_expected_result));
-}
-
-TEST_F(WebMemoryAggregatorTest, FindCrossProcessAggregationStartNode) {
-  FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{1});
-  FrameNodeImpl* cross_process_child = AddCrossProcessFrameNode(
-      "https://example.com/cross_process.html", Bytes{2}, main_frame);
-  FrameNodeImpl* same_process_child = AddFrameNode(
-      "https://example.com/same_process.html", Bytes{3}, cross_process_child);
-
-  auto origin = url::Origin::Create(GURL("https://example.com"));
-  ASSERT_EQ(GetSameOriginParentOrOpener(cross_process_child, origin),
-            main_frame);
-  ASSERT_EQ(GetSameOriginParentOrOpener(same_process_child, origin),
-            cross_process_child);
-
-  // |cross_process_child| has no ancestor in the same process as it.
-  EXPECT_EQ(FindAggregationStartNode(cross_process_child), cross_process_child);
-
-  // The search starting from |same_process_child| should skip over
-  // |cross_process_child|, which is in a different process, and find
-  // |main_frame| which is in the same process.
-  EXPECT_EQ(FindAggregationStartNode(same_process_child), main_frame);
-}
-
 TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
   FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10});
-  FrameNodeImpl* child_frame = AddFrameNode("https://example.com/iframe.html",
-                                            Bytes{5}, main_frame, "example-id");
+  AddFrameNode("https://example.com/iframe.html", Bytes{5}, main_frame,
+               "example-id");
 
   FrameNodeImpl* opened_frame = AddFrameNodeFromOpener(
       "https://example.com/window/", Bytes{4}, main_frame);
-  FrameNodeImpl* child_of_opened_frame =
-      AddFrameNode("https://example.com/window-iframe.html", Bytes{3},
-                   opened_frame, "example-id2");
+  AddFrameNode("https://example.com/window-iframe.html", Bytes{3}, opened_frame,
+               "example-id2");
   FrameNodeImpl* cross_site_child =
       AddFrameNode("https://cross-site-example.com/window-iframe.html",
                    Bytes{2}, opened_frame, "example-id3");
@@ -563,44 +375,8 @@
   FrameNodeImpl* cross_site_popup = AddCrossBrowsingInstanceFrameNodeFromOpener(
       "https://cross-site-example.com/", Bytes{2}, main_frame);
 
-  // FindAggregationStartNode whould return |main_frame| from any of the
-  // same-site frames.
-  for (auto* frame :
-       {main_frame, child_frame, opened_frame, child_of_opened_frame}) {
-    EXPECT_EQ(FindAggregationStartNode(frame), main_frame) << frame->url();
-  }
-
   WebMemoryAggregator aggregator(main_frame);
 
-  // Test the relationships of each node in the graph.
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
-      main_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, opened_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(
-      GetSameOriginParentOrOpener(opened_frame, aggregator.requesting_origin()),
-      main_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, child_of_opened_frame),
-            NodeAggregationType::kSameOriginAggregationPoint);
-  EXPECT_EQ(GetSameOriginParentOrOpener(child_of_opened_frame,
-                                        aggregator.requesting_origin()),
-            opened_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_site_child),
-            NodeAggregationType::kCrossOriginAggregationPoint);
-  EXPECT_EQ(GetSameOriginParentOrOpener(cross_site_child,
-                                        aggregator.requesting_origin()),
-            opened_frame);
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_site_popup),
-            NodeAggregationType::kInvisible);
-  EXPECT_EQ(GetSameOriginParentOrOpener(cross_site_popup,
-                                        aggregator.requesting_origin()),
-            main_frame);
-
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
@@ -617,24 +393,27 @@
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
 
-  // The two cross-site frames should only be able to see themselves (and their
-  // own children, if they had any). They have the same |bytes| so their
-  // expectations only vary by url.
-  for (auto* frame : {cross_site_child, cross_site_popup}) {
-    const std::string url = frame->url().spec();
-    SCOPED_TRACE(url);
-
-    const FrameNode* start_node = FindAggregationStartNode(frame);
-    EXPECT_EQ(start_node, frame);
-
-    WebMemoryAggregator aggregator(start_node);
-    // Only check the NodeAggregationType of the single node that's iterated
-    // over. Parents of the start node have an undefined aggregation type.
-    EXPECT_EQ(FindNodeAggregationType(&aggregator, start_node),
-              NodeAggregationType::kSameOriginAggregationPoint);
+  {
+    WebMemoryAggregator aggregator(cross_site_child);
 
     auto expected_cross_site_result = CreateExpectedMemoryMeasurement({
-        ExpectedMemoryBreakdown(2, AttributionScope::kWindow, url,
+        ExpectedMemoryBreakdown(22, AttributionScope::kCrossOriginAggregated),
+        ExpectedMemoryBreakdown(
+            2, AttributionScope::kWindow,
+            "https://cross-site-example.com/window-iframe.html", base::nullopt,
+            base::nullopt),
+    });
+    auto cross_site_result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(cross_site_result),
+              MeasurementToJSON(expected_cross_site_result));
+  }
+
+  {
+    WebMemoryAggregator aggregator(cross_site_popup);
+
+    auto expected_cross_site_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(2, AttributionScope::kWindow,
+                                "https://cross-site-example.com/",
                                 base::nullopt, base::nullopt),
     });
     auto cross_site_result = aggregator.AggregateMeasureMemoryResult();
@@ -649,14 +428,10 @@
   // This creates an openee window with pending navigation which should be
   // skipped because it may get its own browsing context group once the
   // navigation completes.
-  FrameNodeImpl* pending_frame =
-      AddFrameNodeFromOpener(base::nullopt, Bytes{4}, main_frame);
+  AddFrameNodeFromOpener(base::nullopt, Bytes{4}, main_frame);
 
   WebMemoryAggregator aggregator(main_frame);
 
-  EXPECT_EQ(FindNodeAggregationType(&aggregator, pending_frame),
-            NodeAggregationType::kInvisible);
-
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
                               "https://example.com/"),
@@ -719,6 +494,110 @@
   worker1->RemoveClientFrame(child_frame);
 }
 
+TEST_F(WebMemoryAggregatorTest, AggregateCrossOriginCallers) {
+  FrameNodeImpl* a_com = AddFrameNode("https://a.com/", Bytes{10});
+  FrameNodeImpl* a_com_iframe =
+      AddFrameNode("https://a.com/iframe", Bytes{20}, a_com, "a_com_iframe");
+  FrameNodeImpl* b_com_iframe1 =
+      AddFrameNode("https://b.com/iframe1", Bytes{30}, a_com, "b_com_iframe1");
+  FrameNodeImpl* b_com_iframe2 = AddFrameNode(
+      "https://b.com/iframe2", Bytes{40}, a_com_iframe, "b_com_iframe2");
+  FrameNodeImpl* c_com_iframe1 = AddFrameNode(
+      "https://c.com/iframe1", Bytes{50}, b_com_iframe1, "c_com_iframe1");
+  FrameNodeImpl* a_com_popup1 =
+      AddFrameNodeFromOpener("https://a.com/popup1", Bytes{60}, c_com_iframe1);
+  FrameNodeImpl* b_com_iframe3 = AddFrameNode(
+      "https://b.com/iframe3", Bytes{70}, a_com_popup1, "b_com_iframe3");
+  AddFrameNode("https://c.com/iframe2", Bytes{80}, b_com_iframe3,
+               "c_com_iframe2");
+  AddFrameNodeFromOpener("https://a.com/popup2", Bytes{90}, b_com_iframe2);
+
+  {
+    WebMemoryAggregator aggregator(a_com_popup1);
+    auto expected_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
+                                "https://a.com/"),
+        ExpectedMemoryBreakdown(20, AttributionScope::kWindow,
+                                "https://a.com/iframe", "a_com_iframe"),
+        ExpectedMemoryBreakdown(40, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "b_com_iframe2"),
+        ExpectedMemoryBreakdown(80, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "b_com_iframe1"),
+        ExpectedMemoryBreakdown(60, AttributionScope::kWindow,
+                                "https://a.com/popup1"),
+        ExpectedMemoryBreakdown(150, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "b_com_iframe3"),
+        ExpectedMemoryBreakdown(90, AttributionScope::kWindow,
+                                "https://a.com/popup2"),
+    });
+    auto result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  }
+
+  {
+    WebMemoryAggregator aggregator(b_com_iframe3);
+    auto expected_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(180, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt),
+        ExpectedMemoryBreakdown(40, AttributionScope::kWindow,
+                                "https://b.com/iframe2"),
+        ExpectedMemoryBreakdown(30, AttributionScope::kWindow,
+                                "https://b.com/iframe1"),
+        ExpectedMemoryBreakdown(50, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "c_com_iframe1"),
+        ExpectedMemoryBreakdown(70, AttributionScope::kWindow,
+                                "https://b.com/iframe3"),
+        ExpectedMemoryBreakdown(80, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "c_com_iframe2"),
+    });
+    auto result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  }
+
+  {
+    WebMemoryAggregator aggregator(c_com_iframe1);
+    auto expected_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(320, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt),
+        ExpectedMemoryBreakdown(50, AttributionScope::kWindow,
+                                "https://c.com/iframe1"),
+        ExpectedMemoryBreakdown(80, AttributionScope::kWindow,
+                                "https://c.com/iframe2"),
+    });
+    auto result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  }
+}
+
+TEST_F(WebMemoryAggregatorTest, AggregateCrossProcessCallers) {
+  FrameNodeImpl* a_com = AddFrameNode("https://a.com/", Bytes{10});
+  FrameNodeImpl* b_com_iframe = AddCrossProcessFrameNode(
+      "https://b.com/iframe", Bytes{30}, a_com, "b_com_iframe");
+  {
+    WebMemoryAggregator aggregator(a_com);
+    auto expected_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
+                                "https://a.com/"),
+        ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt, "b_com_iframe"),
+    });
+    auto result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  }
+
+  {
+    WebMemoryAggregator aggregator(b_com_iframe);
+    auto expected_result = CreateExpectedMemoryMeasurement({
+        ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated,
+                                base::nullopt),
+        ExpectedMemoryBreakdown(30, AttributionScope::kWindow,
+                                "https://b.com/iframe"),
+    });
+    auto result = aggregator.AggregateMeasureMemoryResult();
+    EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
+  }
+}
+
 }  // namespace v8_memory
 
 }  // namespace performance_manager
diff --git a/components/performance_manager/v8_memory/web_memory_impl.cc b/components/performance_manager/v8_memory/web_memory_impl.cc
index f14844a..44e3d379 100644
--- a/components/performance_manager/v8_memory/web_memory_impl.cc
+++ b/components/performance_manager/v8_memory/web_memory_impl.cc
@@ -167,16 +167,6 @@
         .Run("WebMeasureMemoryViaPerformanceManager feature is disabled");
     return;
   }
-  // "Memory measurement allowed" predicate from
-  // https://wicg.github.io/performance-measure-memory/ section 3.2.
-  if (url::Origin::Create(frame->GetURL()) !=
-      url::Origin::Create(frame->GetPageNode()->GetMainFrameNode()->GetURL())) {
-    std::move(bad_message_callback)
-        .Run(
-            "performance.measureUserAgentSpecificMemory called from "
-            "a cross-origin subframe");
-    return;
-  }
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE, base::BindOnce(&CheckIsCrossOriginIsolatedOnUISeq,
                                 frame->GetRenderFrameHostProxy(),
diff --git a/components/policy/core/common/cloud/cloud_policy_manager.cc b/components/policy/core/common/cloud/cloud_policy_manager.cc
index 076f8ac..f7524fa 100644
--- a/components/policy/core/common/cloud/cloud_policy_manager.cc
+++ b/components/policy/core/common/cloud/cloud_policy_manager.cc
@@ -42,6 +42,10 @@
 
 CloudPolicyManager::~CloudPolicyManager() {}
 
+bool CloudPolicyManager::IsClientRegistered() const {
+  return client() && client()->is_registered();
+}
+
 void CloudPolicyManager::Init(SchemaRegistry* registry) {
   ConfigurationPolicyProvider::Init(registry);
 
diff --git a/components/policy/core/common/cloud/cloud_policy_manager.h b/components/policy/core/common/cloud/cloud_policy_manager.h
index c095c37..1720f2a 100644
--- a/components/policy/core/common/cloud/cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/cloud_policy_manager.h
@@ -54,6 +54,10 @@
   CloudPolicyCore* core() { return &core_; }
   const CloudPolicyCore* core() const { return &core_; }
 
+  // Returns true if the underlying CloudPolicyClient is already registered.
+  // Virtual for mocking.
+  virtual bool IsClientRegistered() const;
+
   // ConfigurationPolicyProvider:
   void Init(SchemaRegistry* registry) override;
   void Shutdown() override;
diff --git a/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.cc b/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.cc
index 6c9bc208..f97e4927 100644
--- a/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.cc
+++ b/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.cc
@@ -63,10 +63,6 @@
     external_data_manager_->Connect(std::move(url_loader_factory));
 }
 
-bool MachineLevelUserCloudPolicyManager::IsClientRegistered() {
-  return client() && client()->is_registered();
-}
-
 void MachineLevelUserCloudPolicyManager::AddClientObserver(
     CloudPolicyClient::Observer* observer) {
   if (client())
diff --git a/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h b/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h
index 9a36ce4..a00acfd5 100644
--- a/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h
@@ -36,9 +36,6 @@
   void Connect(PrefService* local_state,
                std::unique_ptr<CloudPolicyClient> client);
 
-  // Returns true if the underlying CloudPolicyClient is already registered.
-  bool IsClientRegistered();
-
   // Add or remove |observer| to/from the CloudPolicyClient embedded in |core_|.
   void AddClientObserver(CloudPolicyClient::Observer* observer);
   void RemoveClientObserver(CloudPolicyClient::Observer* observer);
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.cc b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
index 6b4403e..d22bc74e 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
@@ -103,10 +103,6 @@
   SetPoliciesRequired(false);
 }
 
-bool UserCloudPolicyManager::IsClientRegistered() const {
-  return client() && client()->is_registered();
-}
-
 void UserCloudPolicyManager::GetChromePolicy(PolicyMap* policy_map) {
   CloudPolicyManager::GetChromePolicy(policy_map);
 
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.h b/components/policy/core/common/cloud/user_cloud_policy_manager.h
index 4c8acce..f197d2f 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.h
@@ -68,10 +68,6 @@
   // provided by this object until the next time Initialize() is invoked.
   void DisconnectAndRemovePolicy();
 
-  // Returns true if the underlying CloudPolicyClient is already registered.
-  // Virtual for mocking.
-  virtual bool IsClientRegistered() const;
-
   // Creates a CloudPolicyClient for this client. Used in situations where
   // callers want to create a DMToken without actually initializing the
   // profile's policy infrastructure (for example, during signin when we
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 641e2d9..c2cebe1 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -19437,7 +19437,8 @@
                 'window'
               ]
             },
-            'create_desktop_shortcut': { 'type': 'boolean' }
+            'create_desktop_shortcut': { 'type': 'boolean' },
+            'fallback_app_name': { 'type': 'string' }
           },
           'required': ['url']
         }
@@ -19454,13 +19455,33 @@
       }, {
         'url': 'https://docs.google.com',
         'default_launch_container': 'tab'
+      }, {
+        'url': 'https://docs.google.com/editor',
+        'default_launch_container': 'window',
+        'fallback_app_name': 'Editor'
       }],
       'id': 468,
       'caption': '''Configure list of force-installed Web Apps''',
       'tags': [],
       'desc': '''Setting the policy specifies a list of web apps that install silently, without user interaction, and which users can't uninstall or turn off.
 
-      Each list item of the policy is an object with a mandatory member: <ph name="URL_LABEL">url</ph> (the URL of the web app to install) and 2 optional members: <ph name="DEFAULT_LAUNCH_CONTAINER_LABEL">default_launch_container</ph> (for how the web app opens—a new tab is the default) and <ph name="CREATE_DESKTOP_SHORTCUT_LABEL">create_desktop_shortcut</ph> (True if you want to create <ph name="LINUX_OS_NAME">Linux</ph> and Windows® desktop shortcuts).
+      Each list item of the policy is an object with a mandatory member:
+      <ph name="URL_LABEL">url</ph> (the URL of the web app to install)
+
+      and 3 optional members:
+      - <ph name="DEFAULT_LAUNCH_CONTAINER_LABEL">default_launch_container</ph>
+      (for how the web app opens—a new tab is the default)
+
+      - <ph name="CREATE_DESKTOP_SHORTCUT_LABEL">create_desktop_shortcut</ph>
+      (True if you want to create <ph name="LINUX_OS_NAME">Linux</ph> and
+      <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> desktop shortcuts).
+
+      - <ph name="FALLBACK_APP_NAME_LABEL">fallback_app_name</ph>
+      (Starting with <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 90,
+      allows you to override the app name if it is not a
+      Progressive Web App (PWA), or the app name that is temporarily
+      installed if it is a PWA but authentication is required before the
+      installation can be completed.)
 
       See <ph name="PINNED_LAUNCHER_APPS_POLICY_NAME">PinnedLauncherApps</ph> for pinning apps to the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> shelf.''',
       'label': '''URLs for Web Apps to be silently installed.''',
diff --git a/components/printing/common/cloud_print_cdd_conversion.cc b/components/printing/common/cloud_print_cdd_conversion.cc
index 9145586..f5c17bb 100644
--- a/components/printing/common/cloud_print_cdd_conversion.cc
+++ b/components/printing/common/cloud_print_cdd_conversion.cc
@@ -18,11 +18,6 @@
 #include "printing/backend/print_backend.h"
 #include "printing/mojom/print.mojom.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/feature_list.h"
-#include "printing/printing_features.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
 namespace printer = cloud_devices::printer;
 
 namespace cloud_print {
@@ -241,9 +236,7 @@
   pin.set_value(semantic_info.pin_supported);
   pin.SaveTo(&description);
 
-  if (base::FeatureList::IsEnabled(
-          printing::features::kAdvancedPpdAttributes) &&
-      !semantic_info.advanced_capabilities.empty()) {
+  if (!semantic_info.advanced_capabilities.empty()) {
     printer::VendorCapabilities vendor_capabilities =
         GetVendorCapabilities(semantic_info);
     vendor_capabilities.SaveTo(&description);
diff --git a/components/services/app_service/app_service_impl.cc b/components/services/app_service/app_service_impl.cc
index 6395f0e..be867d0 100644
--- a/components/services/app_service/app_service_impl.cc
+++ b/components/services/app_service/app_service_impl.cc
@@ -192,12 +192,13 @@
                             const std::string& app_id,
                             int32_t event_flags,
                             apps::mojom::LaunchSource launch_source,
-                            int64_t display_id) {
+                            apps::mojom::WindowInfoPtr window_info) {
   auto iter = publishers_.find(app_type);
   if (iter == publishers_.end()) {
     return;
   }
-  iter->second->Launch(app_id, event_flags, launch_source, display_id);
+  iter->second->Launch(app_id, event_flags, launch_source,
+                       std::move(window_info));
 }
 void AppServiceImpl::LaunchAppWithFiles(apps::mojom::AppType app_type,
                                         const std::string& app_id,
@@ -219,13 +220,13 @@
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
+    apps::mojom::WindowInfoPtr window_info) {
   auto iter = publishers_.find(app_type);
   if (iter == publishers_.end()) {
     return;
   }
   iter->second->LaunchAppWithIntent(app_id, event_flags, std::move(intent),
-                                    launch_source, display_id);
+                                    launch_source, std::move(window_info));
 }
 
 void AppServiceImpl::SetPermission(apps::mojom::AppType app_type,
diff --git a/components/services/app_service/app_service_impl.h b/components/services/app_service/app_service_impl.h
index a75d357..c717a83b 100644
--- a/components/services/app_service/app_service_impl.h
+++ b/components/services/app_service/app_service_impl.h
@@ -57,7 +57,7 @@
               const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              apps::mojom::WindowInfoPtr window_info) override;
   void LaunchAppWithFiles(apps::mojom::AppType app_type,
                           const std::string& app_id,
                           apps::mojom::LaunchContainer container,
@@ -69,7 +69,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void SetPermission(apps::mojom::AppType app_type,
                      const std::string& app_id,
                      apps::mojom::PermissionPtr permission) override;
diff --git a/components/services/app_service/app_service_impl_unittest.cc b/components/services/app_service/app_service_impl_unittest.cc
index 00596f7..ed7fb8b 100644
--- a/components/services/app_service/app_service_impl_unittest.cc
+++ b/components/services/app_service/app_service_impl_unittest.cc
@@ -107,7 +107,7 @@
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override {}
+              apps::mojom::WindowInfoPtr window_info) override {}
 
   void CallOnApps(apps::mojom::Subscriber* subscriber,
                   std::vector<std::string>& app_ids,
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn
index d4b27ce..ba640fb 100644
--- a/components/services/app_service/public/cpp/BUILD.gn
+++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -191,7 +191,6 @@
     ":preferred_apps",
     ":publisher",
     ":test_support",
-    "//chrome/test:test_support",
     "//content/test:test_support",
     "//testing/gtest",
   ]
@@ -202,6 +201,9 @@
       "instance_update_unittest.cc",
     ]
 
-    deps += [ ":instance_update" ]
+    deps += [
+      ":instance_update",
+      "//chrome/test:test_support",
+    ]
   }
 }
diff --git a/components/services/app_service/public/cpp/publisher_base.cc b/components/services/app_service/public/cpp/publisher_base.cc
index 69e727aa..298df6a 100644
--- a/components/services/app_service/public/cpp/publisher_base.cc
+++ b/components/services/app_service/public/cpp/publisher_base.cc
@@ -105,11 +105,12 @@
   NOTIMPLEMENTED();
 }
 
-void PublisherBase::LaunchAppWithIntent(const std::string& app_id,
-                                        int32_t event_flags,
-                                        apps::mojom::IntentPtr intent,
-                                        apps::mojom::LaunchSource launch_source,
-                                        int64_t display_id) {
+void PublisherBase::LaunchAppWithIntent(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::IntentPtr intent,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
   NOTIMPLEMENTED();
 }
 
diff --git a/components/services/app_service/public/cpp/publisher_base.h b/components/services/app_service/public/cpp/publisher_base.h
index c9450df..6d4d4cb 100644
--- a/components/services/app_service/public/cpp/publisher_base.h
+++ b/components/services/app_service/public/cpp/publisher_base.h
@@ -68,7 +68,7 @@
                            int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
-                           int64_t display_id) override;
+                           apps::mojom::WindowInfoPtr window_info) override;
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission) override;
   void Uninstall(const std::string& app_id,
diff --git a/components/services/app_service/public/mojom/app_service.mojom b/components/services/app_service/public/mojom/app_service.mojom
index e19925e..a7dd445 100644
--- a/components/services/app_service/public/mojom/app_service.mojom
+++ b/components/services/app_service/public/mojom/app_service.mojom
@@ -37,7 +37,7 @@
       string app_id,
       int32 event_flags,
       LaunchSource launch_source,
-      int64 display_id);
+      WindowInfo? window_info);
 
   // Launches an app with |app_id| and |file_path|
   LaunchAppWithFiles(
@@ -56,7 +56,7 @@
       int32 event_flags,
       Intent intent,
       LaunchSource launch_source,
-      int64 display_id);
+      WindowInfo? window_info);
 
   SetPermission(
       AppType app_type,
@@ -148,7 +148,7 @@
       string app_id,
       int32 event_flags,
       LaunchSource launch_source,
-      int64 display_id);
+      WindowInfo? window_info);
 
   // Launches an app with |app_id| and |file_path|
   LaunchAppWithFiles(
@@ -165,7 +165,7 @@
     int32 event_flags,
     Intent intent,
     LaunchSource launch_source,
-    int64 display_id);
+    WindowInfo? window_info);
 
   SetPermission(
       string app_id,
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index 7e7b650..537e3a7f 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -416,3 +416,19 @@
   // When adding new fields, also update the Merge method and other helpers in
   // components/services/app_service/public/cpp/access_update.*
 };
+
+// The window bounds information.
+struct Rect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+};
+
+// The window information to launch an app.
+struct WindowInfo {
+  int32 window_id = -1;
+  int32 state = -1;
+  int64 display_id = -1;
+  Rect? bounds;
+};
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.cc b/components/startup_metric_utils/browser/startup_metric_utils.cc
index adc1289..1fca1d3 100644
--- a/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -572,23 +572,6 @@
                                       g_application_start_ticks, ticks);
 }
 
-void RecordBrowserWindowFirstPaintCompositingEnded(
-    const base::TimeTicks ticks) {
-  DCHECK(!g_application_start_ticks.is_null());
-
-  static bool is_first_call = true;
-  if (!is_first_call || ticks.is_null())
-    return;
-  is_first_call = false;
-  if (!ShouldLogStartupHistogram())
-    return;
-
-  UmaHistogramWithTraceAndTemperature(
-      &base::UmaHistogramLongTimes100,
-      "Startup.BrowserWindow.FirstPaint.CompositingEnded",
-      g_application_start_ticks, ticks);
-}
-
 base::TimeTicks MainEntryPointTicks() {
   return g_chrome_main_entry_ticks;
 }
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.h b/components/startup_metric_utils/browser/startup_metric_utils.h
index d1896be6..ca65780a 100644
--- a/components/startup_metric_utils/browser/startup_metric_utils.h
+++ b/components/startup_metric_utils/browser/startup_metric_utils.h
@@ -76,12 +76,6 @@
 // computes time deltas based on application start time.
 void RecordBrowserWindowFirstPaint(base::TimeTicks ticks);
 
-// Call this with the time when the Browser window painted its children for the
-// first time and we got a CompositingEnded after that. Must be called after
-// RecordApplicationStartTime(), because it computes time deltas based on
-// application start time.
-void RecordBrowserWindowFirstPaintCompositingEnded(base::TimeTicks ticks);
-
 // Returns the TimeTicks corresponding to main entry as recorded by
 // |RecordMainEntryPointTime|. Returns a null TimeTicks if a value has not been
 // recorded yet. This method is expected to be called from the UI thread.
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index 65678f97..73ab249 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -20,6 +20,7 @@
 #include "components/google/core/common/google_util.h"
 #include "components/language/core/browser/url_language_histogram.h"
 #include "components/translate/content/browser/content_record_page_language.h"
+#include "components/translate/content/browser/translate_model_service.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/translate/core/browser/translate_metrics_logger.h"
@@ -57,13 +58,15 @@
 
 ContentTranslateDriver::ContentTranslateDriver(
     content::NavigationController* nav_controller,
-    language::UrlLanguageHistogram* url_language_histogram)
+    language::UrlLanguageHistogram* url_language_histogram,
+    translate::TranslateModelService* translate_model_service)
     : content::WebContentsObserver(nav_controller->GetWebContents()),
       navigation_controller_(nav_controller),
       translate_manager_(nullptr),
       max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
       next_page_seq_no_(0),
-      language_histogram_(url_language_histogram) {
+      language_histogram_(url_language_histogram),
+      translate_model_service_(translate_model_service) {
   DCHECK(navigation_controller_);
 }
 
@@ -333,4 +336,22 @@
     observer.OnPageTranslated(original_lang, translated_lang, error_type);
 }
 
+void ContentTranslateDriver::GetLanguageDetectionModel(
+    GetLanguageDetectionModelCallback callback) {
+  if (!translate_model_service_) {
+    std::move(callback).Run(base::File());
+    return;
+  }
+  translate_model_service_->GetLanguageDetectionModelFile(
+      base::BindOnce(&ContentTranslateDriver::OnLanguageDetectionModelFile,
+                     weak_pointer_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ContentTranslateDriver::OnLanguageDetectionModelFile(
+    GetLanguageDetectionModelCallback callback,
+    base::File model_file) {
+  DCHECK(model_file.IsValid());
+  std::move(callback).Run(std::move(model_file));
+}
+
 }  // namespace translate
diff --git a/components/translate/content/browser/content_translate_driver.h b/components/translate/content/browser/content_translate_driver.h
index 6a3cd22..cdb7c03a 100644
--- a/components/translate/content/browser/content_translate_driver.h
+++ b/components/translate/content/browser/content_translate_driver.h
@@ -34,6 +34,7 @@
 
 struct LanguageDetectionDetails;
 class TranslateManager;
+class TranslateModelService;
 
 // Content implementation of TranslateDriver.
 class ContentTranslateDriver : public TranslateDriver,
@@ -55,9 +56,9 @@
     }
   };
 
-  ContentTranslateDriver(
-      content::NavigationController* nav_controller,
-      language::UrlLanguageHistogram* url_language_histogram);
+  ContentTranslateDriver(content::NavigationController* nav_controller,
+                         language::UrlLanguageHistogram* url_language_histogram,
+                         TranslateModelService* translate_model_service);
   ~ContentTranslateDriver() override;
 
   // Adds or removes observers.
@@ -106,12 +107,17 @@
   // Adds a receiver in |receivers_| for the passed |receiver|.
   void AddReceiver(
       mojo::PendingReceiver<translate::mojom::ContentTranslateDriver> receiver);
+
   // Called when a page has been loaded and can be potentially translated.
   void RegisterPage(
       mojo::PendingRemote<translate::mojom::TranslateAgent> translate_agent,
       const translate::LanguageDetectionDetails& details,
       bool page_level_translation_critiera_met) override;
 
+  // translate::mojom::ContentTranslateDriver implementation:
+  void GetLanguageDetectionModel(
+      GetLanguageDetectionModelCallback callback) override;
+
  protected:
   const base::ObserverList<TranslationObserver, true>& translation_observers()
       const {
@@ -132,6 +138,11 @@
   void InitiateTranslationIfReload(
       content::NavigationHandle* navigation_handle);
 
+  // Runs the provided callback with the loaded model file
+  // to pass it to the connected translate agent.
+  void OnLanguageDetectionModelFile(GetLanguageDetectionModelCallback callback,
+                                    base::File model_file);
+
   // The navigation controller of the tab we are associated with.
   content::NavigationController* navigation_controller_;
 
@@ -163,6 +174,10 @@
   // page language is determined.
   base::TimeTicks finish_navigation_time_;
 
+  // The service that provides the model files needed for translate. Not owned
+  // but guaranteed to outlive |this|.
+  TranslateModelService* const translate_model_service_;
+
   base::WeakPtrFactory<ContentTranslateDriver> weak_pointer_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ContentTranslateDriver);
diff --git a/components/translate/content/browser/per_frame_content_translate_driver.cc b/components/translate/content/browser/per_frame_content_translate_driver.cc
index f3d1244..f53051f 100644
--- a/components/translate/content/browser/per_frame_content_translate_driver.cc
+++ b/components/translate/content/browser/per_frame_content_translate_driver.cc
@@ -130,7 +130,9 @@
 PerFrameContentTranslateDriver::PerFrameContentTranslateDriver(
     content::NavigationController* nav_controller,
     language::UrlLanguageHistogram* url_language_histogram)
-    : ContentTranslateDriver(nav_controller, url_language_histogram) {}
+    : ContentTranslateDriver(nav_controller,
+                             url_language_histogram,
+                             /*translate_model_service=*/nullptr) {}
 
 PerFrameContentTranslateDriver::~PerFrameContentTranslateDriver() = default;
 
diff --git a/components/translate/content/common/translate.mojom b/components/translate/content/common/translate.mojom
index 7c0ece4..10a419c 100644
--- a/components/translate/content/common/translate.mojom
+++ b/components/translate/content/common/translate.mojom
@@ -4,6 +4,7 @@
 
 module translate.mojom;
 
+import "mojo/public/mojom/base/file.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "url/mojom/url.mojom";
@@ -78,4 +79,10 @@
   // and the language for it has been determined.
   RegisterPage(pending_remote<TranslateAgent> translate_agent,
                LanguageDetectionDetails details, bool translation_critiera_met);
+
+  // Request that the language detection model being loaded and returned
+  // for use by the TranslateAgent.
+  GetLanguageDetectionModel()
+        => (mojo_base.mojom.File? model_file);
+
 };
diff --git a/components/translate/content/renderer/translate_agent.cc b/components/translate/content/renderer/translate_agent.cc
index d03e20e..7e2e16e2 100644
--- a/components/translate/content/renderer/translate_agent.cc
+++ b/components/translate/content/renderer/translate_agent.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_script_source.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 #include "v8/include/v8.h"
 
 using blink::WebDocument;
@@ -82,6 +83,19 @@
       extension_scheme_(extension_scheme) {
   translate_task_runner_ = this->render_frame()->GetTaskRunner(
       blink::TaskType::kInternalTranslation);
+
+  if (translate::IsTFLiteLanguageDetectionEnabled()) {
+    translate::LanguageDetectionModel& language_detection_model =
+        GetLanguageDetectionModel();
+    if (!language_detection_model.IsAvailable()) {
+      // TODO(crbug.com/1160948): Consider tracking if another agent associated
+      // with the same LanguageDetectionModel has already requested a model be
+      // provided by the translate host.
+      GetTranslateHandler()->GetLanguageDetectionModel(
+          base::BindOnce(&TranslateAgent::UpdateLanguageDetectionModel,
+                         weak_pointer_factory_.GetWeakPtr()));
+    }
+  }
 }
 
 TranslateAgent::~TranslateAgent() {}
@@ -99,7 +113,7 @@
   // original intent of http-equiv to be an equivalent) with the former
   // being the language of the document and the latter being the
   // language of the intended audience (a distinction really only
-  // relevant for things like langauge textbooks).  This distinction
+  // relevant for things like language textbooks).  This distinction
   // shouldn't affect translation.
   WebLocalFrame* main_frame = render_frame()->GetWebFrame();
   if (!main_frame)
@@ -115,6 +129,12 @@
 
   std::string language;
   if (translate::IsTFLiteLanguageDetectionEnabled()) {
+    if (!document.Url().ProtocolIs(url::kHttpsScheme) &&
+        !document.Url().ProtocolIs(url::kHttpScheme)) {
+      // TFLite-based language detection only supports HTTP/HTTPS pages.
+      // Others should be ignored, for example the New Tab Page.
+      return;
+    }
     translate::LanguageDetectionModel& language_detection_model =
         GetLanguageDetectionModel();
     bool is_available = language_detection_model.IsAvailable();
@@ -502,4 +522,10 @@
          base::GetQuotedJSONString(target_lang) + ")";
 }
 
+void TranslateAgent::UpdateLanguageDetectionModel(base::File model_file) {
+  translate::LanguageDetectionModel& language_detection_model =
+      GetLanguageDetectionModel();
+  language_detection_model.UpdateWithFile(std::move(model_file));
+}
+
 }  // namespace translate
diff --git a/components/translate/content/renderer/translate_agent.h b/components/translate/content/renderer/translate_agent.h
index 162684e..0045888 100644
--- a/components/translate/content/renderer/translate_agent.h
+++ b/components/translate/content/renderer/translate_agent.h
@@ -154,6 +154,10 @@
   // if the page is being closed.
   blink::WebLocalFrame* GetMainFrame();
 
+  // Called by the translate host when a new language detection model file
+  // has been loaded and is available.
+  void UpdateLanguageDetectionModel(base::File model_file);
+
   // The states associated with the current translation.
   TranslateFrameCallback translate_callback_pending_;
   std::string source_lang_;
@@ -184,6 +188,9 @@
   // Method factory used to make calls to TranslatePageImpl.
   base::WeakPtrFactory<TranslateAgent> weak_method_factory_{this};
 
+  // Weak pointer factory used to provide references to the translate host.
+  base::WeakPtrFactory<TranslateAgent> weak_pointer_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(TranslateAgent);
 };
 
diff --git a/components/viz/common/gpu/context_lost_reason.h b/components/viz/common/gpu/context_lost_reason.h
index 9a4ea393..b285d3a4 100644
--- a/components/viz/common/gpu/context_lost_reason.h
+++ b/components/viz/common/gpu/context_lost_reason.h
@@ -31,9 +31,10 @@
   CONTEXT_LOST_SET_DRAW_RECTANGLE_FAILED = 14,
   CONTEXT_LOST_DIRECT_COMPOSITION_OVERLAY_FAILED = 15,
   CONTEXT_LOST_SWAP_FAILED = 16,
+  CONTEXT_LOST_BEGIN_PAINT_FAILED = 17,
   // Update kMaxValue here and <enum name="ContextLostReason"> in
   // tools/metrics/histograms/enum.xml when adding new values.
-  kMaxValue = CONTEXT_LOST_SWAP_FAILED
+  kMaxValue = CONTEXT_LOST_BEGIN_PAINT_FAILED
 };
 
 VIZ_COMMON_EXPORT ContextLostReason
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 701d66d..85a1800 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -39,6 +39,12 @@
     "display/display_damage_tracker.h",
     "display/display_resource_provider.cc",
     "display/display_resource_provider.h",
+    "display/display_resource_provider_gl.cc",
+    "display/display_resource_provider_gl.h",
+    "display/display_resource_provider_skia.cc",
+    "display/display_resource_provider_skia.h",
+    "display/display_resource_provider_software.cc",
+    "display/display_resource_provider_software.h",
     "display/display_scheduler.cc",
     "display/display_scheduler.h",
     "display/display_scheduler_base.cc",
@@ -61,6 +67,8 @@
     "display/gl_renderer_draw_cache.h",
     "display/layer_quad.cc",
     "display/layer_quad.h",
+    "display/null_renderer.cc",
+    "display/null_renderer.h",
     "display/output_surface.cc",
     "display/output_surface.h",
     "display/output_surface_client.h",
@@ -513,7 +521,9 @@
     "display/delegated_ink_point_pixel_test_helper.cc",
     "display/delegated_ink_point_pixel_test_helper.h",
     "display/display_damage_tracker_unittest.cc",
-    "display/display_resource_provider_unittest.cc",
+    "display/display_resource_provider_gl_unittest.cc",
+    "display/display_resource_provider_skia_unittest.cc",
+    "display/display_resource_provider_software_unittest.cc",
     "display/display_scheduler_unittest.cc",
     "display/display_unittest.cc",
     "display/draw_polygon_unittest.cc",
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 8ddbd7fc..8a000558 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -30,8 +30,12 @@
 #include "components/viz/service/display/damage_frame_annotator.h"
 #include "components/viz/service/display/direct_renderer.h"
 #include "components/viz/service/display/display_client.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/null_renderer.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/renderer_utils.h"
 #include "components/viz/service/display/skia_output_surface.h"
@@ -511,32 +515,43 @@
 }
 
 void Display::InitializeRenderer(bool enable_shared_images) {
-  bool uses_gpu_resources = output_surface_->context_provider() ||
-                            skia_output_surface_ ||
-                            output_surface_->capabilities().skips_draw;
-
-  resource_provider_ = std::make_unique<DisplayResourceProvider>(
-      uses_gpu_resources ? DisplayResourceProvider::kGpu
-                         : DisplayResourceProvider::kSoftware,
-      output_surface_->context_provider(), bitmap_manager_,
-      enable_shared_images);
   if (skia_output_surface_) {
+    auto resource_provider =
+        std::make_unique<DisplayResourceProviderSkia>(bitmap_manager_);
     renderer_ = std::make_unique<SkiaRenderer>(
         &settings_, debug_settings_, output_surface_.get(),
-        resource_provider_.get(), overlay_processor_.get(),
+        resource_provider.get(), overlay_processor_.get(),
         skia_output_surface_);
+    resource_provider_ = std::move(resource_provider);
   } else if (output_surface_->context_provider()) {
+    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), bitmap_manager_,
+        enable_shared_images);
     renderer_ = std::make_unique<GLRenderer>(
         &settings_, debug_settings_, output_surface_.get(),
-        resource_provider_.get(), overlay_processor_.get(),
+        resource_provider.get(), overlay_processor_.get(),
         current_task_runner_);
+    resource_provider_ = std::move(resource_provider);
+  } else if (output_surface_->capabilities().skips_draw) {
+    // We use DisplayResourceProviderGL because the actual resources are gpu
+    // backed.
+    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), bitmap_manager_,
+        enable_shared_images);
+    renderer_ = std::make_unique<NullRenderer>(
+        &settings_, debug_settings_, output_surface_.get(),
+        resource_provider.get(), overlay_processor_.get());
+    resource_provider_ = std::move(resource_provider);
   } else {
+    auto resource_provider =
+        std::make_unique<DisplayResourceProviderSoftware>(bitmap_manager_);
     DCHECK(!overlay_processor_->IsOverlaySupported());
     auto renderer = std::make_unique<SoftwareRenderer>(
         &settings_, debug_settings_, output_surface_.get(),
-        resource_provider_.get(), overlay_processor_.get());
+        resource_provider.get(), overlay_processor_.get());
     software_renderer_ = renderer.get();
     renderer_ = std::move(renderer);
+    resource_provider_ = std::move(resource_provider);
   }
 
   renderer_->Initialize();
diff --git a/components/viz/service/display/display_damage_tracker_unittest.cc b/components/viz/service/display/display_damage_tracker_unittest.cc
index d0d0a30..a96a350 100644
--- a/components/viz/service/display/display_damage_tracker_unittest.cc
+++ b/components/viz/service/display/display_damage_tracker_unittest.cc
@@ -11,7 +11,7 @@
 #include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/surface_aggregator.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -31,10 +31,7 @@
  public:
   DisplayDamageTrackerTest()
       : manager_(&shared_bitmap_manager_),
-        resource_provider_(DisplayResourceProvider::kSoftware,
-                           nullptr,
-                           &shared_bitmap_manager_,
-                           false),
+        resource_provider_(&shared_bitmap_manager_),
         aggregator_(manager_.surface_manager(),
                     &resource_provider_,
                     false,
@@ -117,7 +114,7 @@
 
   ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
-  DisplayResourceProvider resource_provider_;
+  DisplayResourceProviderSoftware resource_provider_;
   SurfaceAggregator aggregator_;
   Client root_client_;
   scoped_refptr<base::NullTaskRunner> task_runner_;
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index dede041..f5d6b08 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -67,12 +67,6 @@
     kGpu,
     kSoftware,
   };
-  // TODO(cblume, crbug.com/900973): |enable_shared_images| is a temporary
-  // solution that unblocks us until SharedImages are threadsafe in WebView.
-  DisplayResourceProvider(Mode mode,
-                          ContextProvider* compositor_context_provider,
-                          SharedBitmapManager* shared_bitmap_manager,
-                          bool enable_shared_images = true);
   ~DisplayResourceProvider() override;
 
   DisplayResourceProvider(const DisplayResourceProvider&) = delete;
@@ -360,6 +354,14 @@
   // WebView it happens only when Android calls us on RenderThread.
   void SetAllowAccessToGPUThread(bool allow);
 
+ protected:
+  // TODO(cblume, crbug.com/900973): |enable_shared_images| is a temporary
+  // solution that unblocks us until SharedImages are threadsafe in WebView.
+  DisplayResourceProvider(Mode mode,
+                          ContextProvider* compositor_context_provider,
+                          SharedBitmapManager* shared_bitmap_manager,
+                          bool enable_shared_images = true);
+
  private:
   friend class ScopedAllowGpuAccessForDisplayResourceProvider;
   enum DeleteStyle {
diff --git a/components/viz/service/display/display_resource_provider_gl.cc b/components/viz/service/display/display_resource_provider_gl.cc
new file mode 100644
index 0000000..d756fe0
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_gl.cc
@@ -0,0 +1,18 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_resource_provider_gl.h"
+
+namespace viz {
+
+DisplayResourceProviderGL::DisplayResourceProviderGL(
+    ContextProvider* compositor_context_provider,
+    SharedBitmapManager* shared_bitmap_manager,
+    bool enable_shared_images)
+    : DisplayResourceProvider(DisplayResourceProvider::kGpu,
+                              compositor_context_provider,
+                              shared_bitmap_manager,
+                              enable_shared_images) {}
+
+}  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_gl.h b/components/viz/service/display/display_resource_provider_gl.h
new file mode 100644
index 0000000..0004ba9
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_gl.h
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
+
+#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// DisplayResourceProvider implementation used with GLRenderer.
+class VIZ_SERVICE_EXPORT DisplayResourceProviderGL
+    : public DisplayResourceProvider {
+ public:
+  DisplayResourceProviderGL(ContextProvider* compositor_context_provider,
+                            SharedBitmapManager* shared_bitmap_manager,
+                            bool enable_shared_images = true);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
diff --git a/components/viz/service/display/display_resource_provider_unittest.cc b/components/viz/service/display/display_resource_provider_gl_unittest.cc
similarity index 63%
rename from components/viz/service/display/display_resource_provider_unittest.cc
rename to components/viz/service/display/display_resource_provider_gl_unittest.cc
index d0887c6e..54cffae1 100644
--- a/components/viz/service/display/display_resource_provider_unittest.cc
+++ b/components/viz/service/display/display_resource_provider_gl_unittest.cc
@@ -1,38 +1,30 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
-#include <map>
 #include <memory>
 #include <set>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/check.h"
-#include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory_mapping.h"
 #include "build/build_config.h"
-#include "cc/test/render_pass_test_utils.h"
-#include "cc/test/resource_provider_test_utils.h"
 #include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/resources/bitmap_allocation.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/returned_resource.h"
-#include "components/viz/common/resources/shared_bitmap.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "components/viz/service/display/shared_bitmap_manager.h"
 #include "components/viz/test/test_context_provider.h"
 #include "components/viz/test/test_gles2_interface.h"
-#include "components/viz/test/test_gpu_memory_buffer_manager.h"
 #include "components/viz/test/test_shared_bitmap_manager.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -40,7 +32,6 @@
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/gpu_memory_buffer.h"
 
 using testing::_;
 using testing::ByMove;
@@ -62,30 +53,11 @@
   return other == sync_token;
 }
 
-MATCHER_P(SamePtr, ptr_to_expected, "") {
-  return arg.get() == ptr_to_expected;
-}
-
 static void CollectResources(std::vector<ReturnedResource>* array,
                              const std::vector<ReturnedResource>& returned) {
   array->insert(array->end(), returned.begin(), returned.end());
 }
 
-static SharedBitmapId CreateAndFillSharedBitmap(SharedBitmapManager* manager,
-                                                const gfx::Size& size,
-                                                ResourceFormat format,
-                                                uint32_t value) {
-  SharedBitmapId shared_bitmap_id = SharedBitmap::GenerateId();
-
-  base::MappedReadOnlyRegion shm =
-      bitmap_allocation::AllocateSharedBitmap(size, RGBA_8888);
-  manager->ChildAllocatedSharedBitmap(shm.region.Map(), shared_bitmap_id);
-  base::span<uint32_t> span =
-      shm.mapping.GetMemoryAsSpan<uint32_t>(size.GetArea());
-  std::fill(span.begin(), span.end(), value);
-  return shared_bitmap_id;
-}
-
 class ResourceProviderGLES2Interface : public TestGLES2Interface {
  public:
   ResourceProviderGLES2Interface() = default;
@@ -109,77 +81,57 @@
   gpu::SyncToken last_waited_sync_token_;
 };
 
-class DisplayResourceProviderTest : public testing::TestWithParam<bool> {
+class DisplayResourceProviderGLTest : public testing::Test {
  public:
-  DisplayResourceProviderTest() : use_gpu_(GetParam()) {
-    if (use_gpu_) {
-      auto gl_owned = std::make_unique<ResourceProviderGLES2Interface>();
-      gl_ = gl_owned.get();
-      context_provider_ = TestContextProvider::Create(std::move(gl_owned));
-      context_provider_->UnboundTestContextGL()
-          ->set_support_texture_format_bgra8888(true);
-      context_provider_->BindToCurrentThread();
+  DisplayResourceProviderGLTest() {
+    auto gl_owned = std::make_unique<ResourceProviderGLES2Interface>();
+    gl_ = gl_owned.get();
+    context_provider_ = TestContextProvider::Create(std::move(gl_owned));
+    context_provider_->UnboundTestContextGL()
+        ->set_support_texture_format_bgra8888(true);
+    context_provider_->BindToCurrentThread();
 
-      child_context_provider_ = TestContextProvider::Create();
-      child_context_provider_->UnboundTestContextGL()
-          ->set_support_texture_format_bgra8888(true);
-      child_context_provider_->BindToCurrentThread();
-      gpu_memory_buffer_manager_ =
-          std::make_unique<TestGpuMemoryBufferManager>();
-    }
+    child_context_provider_ = TestContextProvider::Create();
+    child_context_provider_->UnboundTestContextGL()
+        ->set_support_texture_format_bgra8888(true);
+    child_context_provider_->BindToCurrentThread();
+
     // SharedBitmapManager may always be present, even if gpu compositing.
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
 
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        use_gpu_ ? DisplayResourceProvider::kGpu
-                 : DisplayResourceProvider::kSoftware,
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
         context_provider_.get(), shared_bitmap_manager_.get());
 
-    MakeChildResourceProvider();
-  }
-
-  ~DisplayResourceProviderTest() {
-    if (child_resource_provider_)
-      child_resource_provider_->ShutdownAndReleaseAllResources();
-  }
-
-  bool use_gpu() const { return use_gpu_; }
-
-  void MakeChildResourceProvider() {
     child_resource_provider_ = std::make_unique<ClientResourceProvider>();
   }
 
+  ~DisplayResourceProviderGLTest() override {
+    child_resource_provider_->ShutdownAndReleaseAllResources();
+  }
+
   static ReturnCallback GetReturnCallback(
       std::vector<ReturnedResource>* array) {
     return base::BindRepeating(&CollectResources, array);
   }
 
-  static void SetResourceFilter(DisplayResourceProvider* resource_provider,
+  static void SetResourceFilter(DisplayResourceProviderGL* resource_provider,
                                 ResourceId id,
                                 GLenum filter) {
-    DisplayResourceProvider::ScopedSamplerGL sampler(resource_provider, id,
-                                                     GL_TEXTURE_2D, filter);
+    DisplayResourceProviderGL::ScopedSamplerGL sampler(resource_provider, id,
+                                                       GL_TEXTURE_2D, filter);
   }
 
-
   TransferableResource CreateResource(ResourceFormat format) {
     constexpr gfx::Size size(64, 64);
-    if (use_gpu()) {
-      gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
-      gpu::SyncToken sync_token = GenSyncToken();
-      EXPECT_TRUE(sync_token.HasData());
+    gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
+    gpu::SyncToken sync_token = GenSyncToken();
+    EXPECT_TRUE(sync_token.HasData());
 
-      TransferableResource gl_resource = TransferableResource::MakeGL(
-          gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
-          false /* is_overlay_candidate */);
-      gl_resource.format = format;
-      return gl_resource;
-    } else {
-      SharedBitmapId shared_bitmap_id = CreateAndFillSharedBitmap(
-          shared_bitmap_manager_.get(), size, format, 0);
-
-      return TransferableResource::MakeSoftware(shared_bitmap_id, size, format);
-    }
+    TransferableResource gl_resource = TransferableResource::MakeGL(
+        gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
+        false /* is_overlay_candidate */);
+    gl_resource.format = format;
+    return gl_resource;
   }
 
   ResourceId MakeGpuResourceAndSendToDisplay(
@@ -211,224 +163,16 @@
   }
 
  protected:
-  const bool use_gpu_;
   ResourceProviderGLES2Interface* gl_ = nullptr;
   uint64_t next_fence_sync_ = 1;
   scoped_refptr<TestContextProvider> context_provider_;
   scoped_refptr<TestContextProvider> child_context_provider_;
-  std::unique_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
 };
 
-INSTANTIATE_TEST_SUITE_P(DisplayResourceProviderTests,
-                         DisplayResourceProviderTest,
-                         ::testing::Values(false, true));
-
-class MockExternalUseClient : public ExternalUseClient {
- public:
-  MockExternalUseClient() = default;
-  MOCK_METHOD1(ReleaseImageContexts,
-               gpu::SyncToken(
-                   std::vector<std::unique_ptr<ImageContext>> image_contexts));
-  MOCK_METHOD6(CreateImageContext,
-               std::unique_ptr<ImageContext>(
-                   const gpu::MailboxHolder&,
-                   const gfx::Size&,
-                   ResourceFormat,
-                   bool,
-                   const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info,
-                   sk_sp<SkColorSpace>));
-};
-
-TEST_P(DisplayResourceProviderTest, LockForExternalUse) {
-  // TODO(penghuang): consider supporting SW mode.
-  if (!use_gpu())
-    return;
-
-  gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x123),
-                             0x42);
-  auto mailbox = gpu::Mailbox::Generate();
-  constexpr gfx::Size size(64, 64);
-  TransferableResource gl_resource = TransferableResource::MakeGL(
-      mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token1, size,
-      false /* is_overlay_candidate */);
-  ResourceId id1 = child_resource_provider_->ImportResource(
-      gl_resource, SingleReleaseCallback::Create(base::DoNothing()));
-  std::vector<ReturnedResource> returned_to_child;
-  int child_id =
-      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
-
-  // Transfer some resources to the parent.
-  std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent(
-      {id1}, &list,
-      static_cast<RasterContextProvider*>(child_context_provider_.get()));
-  ASSERT_EQ(1u, list.size());
-  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
-
-  resource_provider_->ReceiveFromChild(child_id, list);
-
-  // In DisplayResourceProvider's namespace, use the mapped resource id.
-  std::unordered_map<ResourceId, ResourceId> resource_map =
-      resource_provider_->GetChildToParentMap(child_id);
-
-  unsigned parent_id = resource_map[list.front().id];
-
-  auto owned_image_context = std::make_unique<ExternalUseClient::ImageContext>(
-      gpu::MailboxHolder(mailbox, sync_token1, GL_TEXTURE_2D), size, RGBA_8888,
-      /*ycbcr_info=*/base::nullopt, /*color_space=*/nullptr);
-  auto* image_context = owned_image_context.get();
-
-  testing::StrictMock<MockExternalUseClient> client;
-  DisplayResourceProvider::LockSetForExternalUse lock_set(
-      resource_provider_.get(), &client);
-  gpu::MailboxHolder holder;
-  EXPECT_CALL(client, CreateImageContext(_, _, _, _, _, _))
-      .WillOnce(DoAll(SaveArg<0>(&holder),
-                      Return(ByMove(std::move(owned_image_context)))));
-
-  ExternalUseClient::ImageContext* locked_image_context = lock_set.LockResource(
-      parent_id, /*maybe_concurrent_reads=*/true, /*is_video_plane=*/false);
-  EXPECT_EQ(image_context, locked_image_context);
-  ASSERT_EQ(holder.mailbox, mailbox);
-  ASSERT_TRUE(holder.sync_token.HasData());
-
-  // Don't release while locked.
-  EXPECT_CALL(client, ReleaseImageContexts(_)).Times(0);
-  // Return the resources back to the child. Nothing should happen because
-  // of the resource lock.
-  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
-  // The resource should not be returned due to the external use lock.
-  EXPECT_EQ(0u, returned_to_child.size());
-
-  gpu::SyncToken sync_token2(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x234),
-                             0x456);
-  sync_token2.SetVerifyFlush();
-
-  gpu::SyncToken sync_token3(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x234),
-                             0x567);
-  sync_token3.SetVerifyFlush();
-  // We will get a second release of |parent_id| now that we've released our
-  // external lock.
-  EXPECT_CALL(client, ReleaseImageContexts(
-                          testing::ElementsAre(SamePtr(locked_image_context))))
-      .WillOnce(Return(sync_token3));
-  // UnlockResources will also call DeclareUsedResourcesFromChild.
-  lock_set.UnlockResources(sync_token2);
-  // The resource should be returned after the lock is released.
-  EXPECT_EQ(1u, returned_to_child.size());
-  EXPECT_EQ(sync_token3, returned_to_child[0].sync_token);
-  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
-  child_resource_provider_->RemoveImportedResource(id1);
-}
-
-TEST_P(DisplayResourceProviderTest, LockForExternalUseWebView) {
-  // TODO(penghuang): consider supporting SW mode.
-  if (!use_gpu())
-    return;
-
-  gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x123),
-                             0x42);
-  auto mailbox = gpu::Mailbox::Generate();
-  constexpr gfx::Size size(64, 64);
-  TransferableResource gl_resource = TransferableResource::MakeGL(
-      mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token1, size,
-      false /* is_overlay_candidate */);
-  ResourceId id1 = child_resource_provider_->ImportResource(
-      gl_resource, SingleReleaseCallback::Create(base::DoNothing()));
-  std::vector<ReturnedResource> returned_to_child;
-  int child_id =
-      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
-
-  // Transfer some resources to the parent.
-  std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent(
-      {id1}, &list,
-      static_cast<RasterContextProvider*>(child_context_provider_.get()));
-  ASSERT_EQ(1u, list.size());
-  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
-
-  resource_provider_->ReceiveFromChild(child_id, list);
-
-  // In DisplayResourceProvider's namespace, use the mapped resource id.
-  std::unordered_map<ResourceId, ResourceId> resource_map =
-      resource_provider_->GetChildToParentMap(child_id);
-
-  unsigned parent_id = resource_map[list.front().id];
-
-  auto owned_image_context = std::make_unique<ExternalUseClient::ImageContext>(
-      gpu::MailboxHolder(mailbox, sync_token1, GL_TEXTURE_2D), size, RGBA_8888,
-      /*ycbcr_info=*/base::nullopt, /*color_space=*/nullptr);
-  auto* image_context = owned_image_context.get();
-
-  testing::StrictMock<MockExternalUseClient> client;
-  DisplayResourceProvider::LockSetForExternalUse lock_set(
-      resource_provider_.get(), &client);
-  gpu::MailboxHolder holder;
-  EXPECT_CALL(client, CreateImageContext(_, _, _, _, _, _))
-      .WillOnce(DoAll(SaveArg<0>(&holder),
-                      Return(ByMove(std::move(owned_image_context)))));
-
-  ExternalUseClient::ImageContext* locked_image_context = lock_set.LockResource(
-      parent_id, /*maybe_concurrent_reads=*/true, /*is_video_plane=*/false);
-  EXPECT_EQ(image_context, locked_image_context);
-  ASSERT_EQ(holder.mailbox, mailbox);
-  ASSERT_TRUE(holder.sync_token.HasData());
-
-  // Don't release while locked.
-  EXPECT_CALL(client, ReleaseImageContexts(_)).Times(0);
-  // Return the resources back to the child. Nothing should happen because
-  // of the resource lock.
-  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
-  // The resource should not be returned due to the external use lock.
-  EXPECT_EQ(0u, returned_to_child.size());
-
-  // Disable access to gpu thread.
-  resource_provider_->SetAllowAccessToGPUThread(false);
-
-  gpu::SyncToken sync_token2(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x234),
-                             0x456);
-  sync_token2.SetVerifyFlush();
-
-  gpu::SyncToken sync_token3(gpu::CommandBufferNamespace::GPU_IO,
-                             gpu::CommandBufferId::FromUnsafeValue(0x234),
-                             0x567);
-  sync_token3.SetVerifyFlush();
-
-  // Without GPU thread access no ReleaseImageContexts() should happen
-  EXPECT_CALL(client, ReleaseImageContexts(_)).Times(0);
-  // Unlock resources
-  lock_set.UnlockResources(sync_token2);
-  // Resources should not be returned because we can't unlock them on GPU
-  // thread.
-  EXPECT_EQ(0u, returned_to_child.size());
-
-  // We will get a second release of |parent_id| now that we've released our
-  // external lock and have access to GPU thread.
-  EXPECT_CALL(client, ReleaseImageContexts(
-                          testing::ElementsAre(SamePtr(locked_image_context))))
-      .WillOnce(Return(sync_token3));
-  // Enable access to GPU Thread
-  resource_provider_->SetAllowAccessToGPUThread(true);
-
-  // The resource should be returned after the lock is released.
-  EXPECT_EQ(1u, returned_to_child.size());
-  EXPECT_EQ(sync_token3, returned_to_child[0].sync_token);
-  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
-  child_resource_provider_->RemoveImportedResource(id1);
-}
-
-TEST_P(DisplayResourceProviderTest, ReadLockCountStopsReturnToChildOrDelete) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReadLockCountStopsReturnToChildOrDelete) {
   MockReleaseCallback release;
   TransferableResource tran = CreateResource(RGBA_8888);
   ResourceId id1 = child_resource_provider_->ImportResource(
@@ -454,8 +198,8 @@
         resource_provider_->GetChildToParentMap(child_id);
     ResourceId mapped_resource_id = resource_map[list[0].id];
     resource_provider_->WaitSyncToken(mapped_resource_id);
-    DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                   mapped_resource_id);
+    DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                     mapped_resource_id);
 
     resource_provider_->DeclareUsedResourcesFromChild(child_id,
                                                       ResourceIdSet());
@@ -489,10 +233,7 @@
   ~TestFence() override = default;
 };
 
-TEST_P(DisplayResourceProviderTest, ReadLockFenceStopsReturnToChildOrDelete) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReadLockFenceStopsReturnToChildOrDelete) {
   MockReleaseCallback release;
   TransferableResource tran1 = CreateResource(RGBA_8888);
   tran1.read_lock_fences_enabled = true;
@@ -524,8 +265,8 @@
   {
     unsigned parent_id = resource_map[list.front().id];
     resource_provider_->WaitSyncToken(parent_id);
-    DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                   parent_id);
+    DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                     parent_id);
   }
   resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
   EXPECT_EQ(0u, returned_to_child.size());
@@ -542,10 +283,7 @@
   child_resource_provider_->RemoveImportedResource(id1);
 }
 
-TEST_P(DisplayResourceProviderTest, ReadLockFenceDestroyChild) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReadLockFenceDestroyChild) {
   MockReleaseCallback release;
 
   TransferableResource tran1 = CreateResource(RGBA_8888);
@@ -582,11 +320,11 @@
   scoped_refptr<TestFence> fence(new TestFence);
   resource_provider_->SetReadLockFence(fence.get());
   {
-    for (size_t i = 0; i < list.size(); i++) {
-      unsigned parent_id = resource_map[list[i].id];
+    for (auto& resource : list) {
+      unsigned parent_id = resource_map[resource.id];
       resource_provider_->WaitSyncToken(parent_id);
-      DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                     parent_id);
+      DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                       parent_id);
     }
   }
   EXPECT_EQ(0u, returned_to_child.size());
@@ -608,10 +346,7 @@
   child_resource_provider_->RemoveImportedResource(id2);
 }
 
-TEST_P(DisplayResourceProviderTest, ReadLockFenceContextLost) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReadLockFenceContextLost) {
   TransferableResource tran1 = CreateResource(RGBA_8888);
   tran1.read_lock_fences_enabled = true;
   ResourceId id1 = child_resource_provider_->ImportResource(
@@ -644,11 +379,11 @@
   scoped_refptr<TestFence> fence(new TestFence);
   resource_provider_->SetReadLockFence(fence.get());
   {
-    for (size_t i = 0; i < list.size(); i++) {
-      unsigned parent_id = resource_map[list[i].id];
+    for (auto& resource : list) {
+      unsigned parent_id = resource_map[resource.id];
       resource_provider_->WaitSyncToken(parent_id);
-      DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                     parent_id);
+      DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                       parent_id);
     }
   }
   EXPECT_EQ(0u, returned_to_child.size());
@@ -668,10 +403,8 @@
 }
 
 // Test that ScopedBatchReturnResources batching works.
-TEST_P(DisplayResourceProviderTest, ScopedBatchReturnResourcesPreventsReturn) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest,
+       ScopedBatchReturnResourcesPreventsReturn) {
   MockReleaseCallback release;
 
   std::vector<ReturnedResource> returned_to_child;
@@ -683,9 +416,9 @@
   constexpr size_t kLockedResources = 3;
   constexpr size_t kUsedResources = 4;
   ResourceId ids[kTotalResources];
-  for (size_t i = 0; i < kTotalResources; i++) {
+  for (auto& id : ids) {
     TransferableResource tran = CreateResource(RGBA_8888);
-    ids[i] = child_resource_provider_->ImportResource(
+    id = child_resource_provider_->ImportResource(
         tran, SingleReleaseCallback::Create(base::BindOnce(
                   &MockReleaseCallback::Released, base::Unretained(&release))));
   }
@@ -704,20 +437,20 @@
   // In DisplayResourceProvider's namespace, use the mapped resource id.
   std::unordered_map<ResourceId, ResourceId> resource_map =
       resource_provider_->GetChildToParentMap(child_id);
-  std::vector<std::unique_ptr<DisplayResourceProvider::ScopedReadLockGL>>
+  std::vector<std::unique_ptr<DisplayResourceProviderGL::ScopedReadLockGL>>
       read_locks;
   for (size_t i = 0; i < kLockedResources; i++) {
     unsigned int mapped_resource_id = resource_map[ids[i]];
     resource_provider_->WaitSyncToken(mapped_resource_id);
     read_locks.push_back(
-        std::make_unique<DisplayResourceProvider::ScopedReadLockGL>(
+        std::make_unique<DisplayResourceProviderGL::ScopedReadLockGL>(
             resource_provider_.get(), mapped_resource_id));
   }
 
   // Mark all locked resources, and one unlocked resource as used for first
   // batch.
   {
-    DisplayResourceProvider::ScopedBatchReturnResources returner(
+    DisplayResourceProviderGL::ScopedBatchReturnResources returner(
         resource_provider_.get());
     resource_provider_->DeclareUsedResourcesFromChild(
         child_id, ResourceIdSet(ids, ids + kUsedResources));
@@ -729,7 +462,7 @@
 
   // Return all locked resources.
   {
-    DisplayResourceProvider::ScopedBatchReturnResources returner(
+    DisplayResourceProviderGL::ScopedBatchReturnResources returner(
         resource_provider_.get());
     resource_provider_->DeclareUsedResourcesFromChild(
         child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
@@ -751,7 +484,7 @@
 
   // Returns from destroying the child is also batched.
   {
-    DisplayResourceProvider::ScopedBatchReturnResources returner(
+    DisplayResourceProviderGL::ScopedBatchReturnResources returner(
         resource_provider_.get());
     resource_provider_->DestroyChild(child_id);
     EXPECT_EQ(0u, returned_to_child.size());
@@ -765,7 +498,7 @@
     child_resource_provider_->RemoveImportedResource(id);
 }
 
-TEST_P(DisplayResourceProviderTest, LostMailboxInParent) {
+TEST_F(DisplayResourceProviderGLTest, LostMailboxInParent) {
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
                             gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
   auto tran = CreateResource(RGBA_8888);
@@ -784,64 +517,7 @@
   ASSERT_EQ(1u, returned_to_child.size());
 
   // Losing an output surface only loses hardware resources.
-  EXPECT_EQ(returned_to_child[0].lost, use_gpu());
-}
-
-TEST_P(DisplayResourceProviderTest, ReadSoftwareResources) {
-  if (use_gpu())
-    return;
-
-  gfx::Size size(64, 64);
-  ResourceFormat format = RGBA_8888;
-  const uint32_t kBadBeef = 0xbadbeef;
-  SharedBitmapId shared_bitmap_id = CreateAndFillSharedBitmap(
-      shared_bitmap_manager_.get(), size, format, kBadBeef);
-
-  auto resource =
-      TransferableResource::MakeSoftware(shared_bitmap_id, size, format);
-
-  MockReleaseCallback release;
-  ResourceId resource_id = child_resource_provider_->ImportResource(
-      resource,
-      SingleReleaseCallback::Create(base::BindOnce(
-          &MockReleaseCallback::Released, base::Unretained(&release))));
-  EXPECT_NE(0u, resource_id);
-
-  // Transfer resources to the parent.
-  std::vector<TransferableResource> send_to_parent;
-  std::vector<ReturnedResource> returned_to_child;
-  int child_id = resource_provider_->CreateChild(
-      base::BindRepeating(&CollectResources, &returned_to_child));
-  child_resource_provider_->PrepareSendToParent(
-      {resource_id}, &send_to_parent,
-      static_cast<RasterContextProvider*>(child_context_provider_.get()));
-  resource_provider_->ReceiveFromChild(child_id, send_to_parent);
-
-  // In DisplayResourceProvider's namespace, use the mapped resource id.
-  std::unordered_map<ResourceId, ResourceId> resource_map =
-      resource_provider_->GetChildToParentMap(child_id);
-  ResourceId mapped_resource_id = resource_map[resource_id];
-
-  {
-    DisplayResourceProvider::ScopedReadLockSkImage lock(
-        resource_provider_.get(), mapped_resource_id);
-    const SkImage* sk_image = lock.sk_image();
-    SkBitmap sk_bitmap;
-    sk_image->asLegacyBitmap(&sk_bitmap);
-    EXPECT_EQ(sk_image->width(), size.width());
-    EXPECT_EQ(sk_image->height(), size.height());
-    EXPECT_EQ(*sk_bitmap.getAddr32(16, 16), kBadBeef);
-  }
-
-  EXPECT_EQ(0u, returned_to_child.size());
-  // Transfer resources back from the parent to the child. Set no resources as
-  // being in use.
-  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
-  EXPECT_EQ(1u, returned_to_child.size());
-  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
-
-  EXPECT_CALL(release, Released(_, false));
-  child_resource_provider_->RemoveImportedResource(resource_id);
+  EXPECT_EQ(returned_to_child[0].lost, true);
 }
 
 class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
@@ -869,9 +545,8 @@
     auto context_provider = TestContextProvider::Create(std::move(gl_owned));
     context_provider->BindToCurrentThread();
 
-    auto resource_provider = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, context_provider.get(),
-        shared_bitmap_manager);
+    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+        context_provider.get(), shared_bitmap_manager);
 
     auto child_gl_owned =
         std::make_unique<TextureStateTrackingGLES2Interface>();
@@ -945,7 +620,7 @@
                                        sampler_filter));
       }
 
-      DisplayResourceProvider::ScopedSamplerGL lock(
+      DisplayResourceProviderGL::ScopedSamplerGL lock(
           resource_provider.get(), mapped_resource_id, sampler_filter);
       testing::Mock::VerifyAndClearExpectations(gl);
 
@@ -972,55 +647,34 @@
   }
 };
 
-TEST_P(DisplayResourceProviderTest, ReceiveGLTexture2D_LinearToLinear) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToLinear) {
   ResourceProviderTestImportedResourceGLFilters::RunTest(
       shared_bitmap_manager_.get(), false, GL_LINEAR);
 }
 
-TEST_P(DisplayResourceProviderTest, ReceiveGLTexture2D_NearestToNearest) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToNearest) {
   ResourceProviderTestImportedResourceGLFilters::RunTest(
       shared_bitmap_manager_.get(), true, GL_NEAREST);
 }
 
-TEST_P(DisplayResourceProviderTest, ReceiveGLTexture2D_NearestToLinear) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToLinear) {
   ResourceProviderTestImportedResourceGLFilters::RunTest(
       shared_bitmap_manager_.get(), true, GL_LINEAR);
 }
 
-TEST_P(DisplayResourceProviderTest, ReceiveGLTexture2D_LinearToNearest) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToNearest) {
   ResourceProviderTestImportedResourceGLFilters::RunTest(
       shared_bitmap_manager_.get(), false, GL_NEAREST);
 }
 
-TEST_P(DisplayResourceProviderTest, ReceiveGLTextureExternalOES) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, ReceiveGLTextureExternalOES) {
   auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
   TextureStateTrackingGLES2Interface* gl = gl_owned.get();
   auto context_provider = TestContextProvider::Create(std::move(gl_owned));
   context_provider->BindToCurrentThread();
 
-  auto resource_provider = std::make_unique<DisplayResourceProvider>(
-      DisplayResourceProvider::kGpu, context_provider.get(),
-      shared_bitmap_manager_.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      context_provider.get(), shared_bitmap_manager_.get());
 
   auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
   TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get();
@@ -1083,8 +737,8 @@
     EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
         .WillOnce(Return(texture_id));
 
-    DisplayResourceProvider::ScopedReadLockGL lock(resource_provider.get(),
-                                                   mapped_resource_id);
+    DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider.get(),
+                                                     mapped_resource_id);
     testing::Mock::VerifyAndClearExpectations(gl);
 
     // When done with it, a sync point should be inserted, but no produce is
@@ -1103,19 +757,14 @@
   child_resource_provider->RemoveImportedResource(resource_id);
 }
 
-TEST_P(DisplayResourceProviderTest, WaitSyncTokenIfNeeded) {
-  // Mailboxing is only supported for GL textures.
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, WaitSyncTokenIfNeeded) {
   auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
   TextureStateTrackingGLES2Interface* gl = gl_owned.get();
   auto context_provider = TestContextProvider::Create(std::move(gl_owned));
   context_provider->BindToCurrentThread();
 
-  auto resource_provider = std::make_unique<DisplayResourceProvider>(
-      DisplayResourceProvider::kGpu, context_provider.get(),
-      shared_bitmap_manager_.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      context_provider.get(), shared_bitmap_manager_.get());
 
   EXPECT_CALL(*gl, BindTexture(_, _)).Times(0);
   EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
@@ -1147,10 +796,7 @@
 }
 
 #if defined(OS_ANDROID)
-TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
-  if (!use_gpu())
-    return;
-
+TEST_F(DisplayResourceProviderGLTest, OverlayPromotionHint) {
   gpu::Mailbox external_mailbox = gpu::Mailbox::Generate();
   gpu::SyncToken external_sync_token = GenSyncToken();
   EXPECT_TRUE(external_sync_token.HasData());
@@ -1196,8 +842,8 @@
   EXPECT_FALSE(resource_provider_->DoAnyResourcesWantPromotionHints());
   {
     resource_provider_->WaitSyncToken(mapped_id1);
-    DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                   mapped_id1);
+    DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                     mapped_id1);
   }
   EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());
   EXPECT_TRUE(resource_provider_->DoAnyResourcesWantPromotionHints());
diff --git a/components/viz/service/display/display_resource_provider_skia.cc b/components/viz/service/display/display_resource_provider_skia.cc
new file mode 100644
index 0000000..2528cad
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_skia.cc
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_resource_provider_skia.h"
+
+namespace viz {
+
+DisplayResourceProviderSkia::DisplayResourceProviderSkia(
+    SharedBitmapManager* shared_bitmap_manager)
+    : DisplayResourceProvider(DisplayResourceProvider::kGpu,
+                              /*compositor_context_provider=*/nullptr,
+                              shared_bitmap_manager,
+                              /*enable_shared_images=*/true) {}
+
+}  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_skia.h b/components/viz/service/display/display_resource_provider_skia.h
new file mode 100644
index 0000000..6e799996
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_skia.h
@@ -0,0 +1,23 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SKIA_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SKIA_H_
+
+#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// DisplayResourceProvider implementation used with SkiaRenderer.
+class VIZ_SERVICE_EXPORT DisplayResourceProviderSkia
+    : public DisplayResourceProvider {
+ public:
+  explicit DisplayResourceProviderSkia(
+      SharedBitmapManager* shared_bitmap_manager);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SKIA_H_
diff --git a/components/viz/service/display/display_resource_provider_skia_unittest.cc b/components/viz/service/display/display_resource_provider_skia_unittest.cc
new file mode 100644
index 0000000..4041aee
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_skia_unittest.cc
@@ -0,0 +1,690 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_resource_provider_skia.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "build/build_config.h"
+#include "components/viz/client/client_resource_provider.h"
+#include "components/viz/common/resources/returned_resource.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
+#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_shared_bitmap_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/geometry/rect.h"
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+namespace viz {
+namespace {
+
+class MockReleaseCallback {
+ public:
+  MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
+};
+
+MATCHER_P(SamePtr, ptr_to_expected, "") {
+  return arg.get() == ptr_to_expected;
+}
+
+static void CollectResources(std::vector<ReturnedResource>* array,
+                             const std::vector<ReturnedResource>& returned) {
+  array->insert(array->end(), returned.begin(), returned.end());
+}
+
+class MockExternalUseClient : public ExternalUseClient {
+ public:
+  MockExternalUseClient() = default;
+  MOCK_METHOD1(ReleaseImageContexts,
+               gpu::SyncToken(
+                   std::vector<std::unique_ptr<ImageContext>> image_contexts));
+  MOCK_METHOD6(CreateImageContext,
+               std::unique_ptr<ImageContext>(
+                   const gpu::MailboxHolder&,
+                   const gfx::Size&,
+                   ResourceFormat,
+                   bool,
+                   const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info,
+                   sk_sp<SkColorSpace>));
+};
+
+class DisplayResourceProviderSkiaTest : public testing::Test {
+ public:
+  DisplayResourceProviderSkiaTest() {
+    child_context_provider_ = TestContextProvider::Create();
+    child_context_provider_->BindToCurrentThread();
+
+    // SharedBitmapManager may always be present, even if gpu compositing.
+    shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
+
+    resource_provider_ = std::make_unique<DisplayResourceProviderSkia>(
+        shared_bitmap_manager_.get());
+
+    lock_set_.emplace(resource_provider_.get(), &client_);
+
+    child_resource_provider_ = std::make_unique<ClientResourceProvider>();
+  }
+
+  ~DisplayResourceProviderSkiaTest() override {
+    child_resource_provider_->ShutdownAndReleaseAllResources();
+  }
+
+  static ReturnCallback GetReturnCallback(
+      std::vector<ReturnedResource>* array) {
+    return base::BindRepeating(&CollectResources, array);
+  }
+
+  TransferableResource CreateResource(ResourceFormat format) {
+    constexpr gfx::Size size(64, 64);
+    gpu::Mailbox gpu_mailbox = gpu::Mailbox::GenerateForSharedImage();
+    gpu::SyncToken sync_token = GenSyncToken();
+    EXPECT_TRUE(sync_token.HasData());
+
+    TransferableResource gl_resource = TransferableResource::MakeGL(
+        gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
+        false /* is_overlay_candidate */);
+    gl_resource.format = format;
+    return gl_resource;
+  }
+
+  gpu::SyncToken GenSyncToken() {
+    gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
+                              gpu::CommandBufferId::FromUnsafeValue(0x123),
+                              next_fence_sync_++);
+    sync_token.SetVerifyFlush();
+    return sync_token;
+  }
+
+ protected:
+  uint64_t next_fence_sync_ = 1;
+  scoped_refptr<TestContextProvider> child_context_provider_;
+  std::unique_ptr<DisplayResourceProviderSkia> resource_provider_;
+  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
+  std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
+  testing::NiceMock<MockExternalUseClient> client_;
+  base::Optional<DisplayResourceProviderSkia::LockSetForExternalUse> lock_set_;
+};
+
+TEST_F(DisplayResourceProviderSkiaTest, LockForExternalUse) {
+  gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x123),
+                             0x42);
+  auto mailbox = gpu::Mailbox::Generate();
+  constexpr gfx::Size size(64, 64);
+  TransferableResource gl_resource = TransferableResource::MakeGL(
+      mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token1, size,
+      false /* is_overlay_candidate */);
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      gl_resource, SingleReleaseCallback::Create(base::DoNothing()));
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer some resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(1u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  unsigned parent_id = resource_map[list.front().id];
+
+  auto owned_image_context = std::make_unique<ExternalUseClient::ImageContext>(
+      gpu::MailboxHolder(mailbox, sync_token1, GL_TEXTURE_2D), size, RGBA_8888,
+      /*ycbcr_info=*/base::nullopt, /*color_space=*/nullptr);
+  auto* image_context = owned_image_context.get();
+
+  gpu::MailboxHolder holder;
+  EXPECT_CALL(client_, CreateImageContext(_, _, _, _, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&holder),
+                      Return(ByMove(std::move(owned_image_context)))));
+
+  ExternalUseClient::ImageContext* locked_image_context =
+      lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                              /*is_video_plane=*/false);
+  EXPECT_EQ(image_context, locked_image_context);
+  ASSERT_EQ(holder.mailbox, mailbox);
+  ASSERT_TRUE(holder.sync_token.HasData());
+
+  // Don't release while locked.
+  EXPECT_CALL(client_, ReleaseImageContexts(_)).Times(0);
+  // Return the resources back to the child. Nothing should happen because
+  // of the resource lock.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  // The resource should not be returned due to the external use lock.
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  gpu::SyncToken sync_token2(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x234),
+                             0x456);
+  sync_token2.SetVerifyFlush();
+
+  gpu::SyncToken sync_token3(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x234),
+                             0x567);
+  sync_token3.SetVerifyFlush();
+  // We will get a second release of |parent_id| now that we've released our
+  // external lock.
+  EXPECT_CALL(client_, ReleaseImageContexts(
+                           testing::ElementsAre(SamePtr(locked_image_context))))
+      .WillOnce(Return(sync_token3));
+  // UnlockResources will also call DeclareUsedResourcesFromChild.
+  lock_set_->UnlockResources(sync_token2);
+  // The resource should be returned after the lock is released.
+  EXPECT_EQ(1u, returned_to_child.size());
+  EXPECT_EQ(sync_token3, returned_to_child[0].sync_token);
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  child_resource_provider_->RemoveImportedResource(id1);
+}
+
+TEST_F(DisplayResourceProviderSkiaTest, LockForExternalUseWebView) {
+  gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x123),
+                             0x42);
+  auto mailbox = gpu::Mailbox::Generate();
+  constexpr gfx::Size size(64, 64);
+  TransferableResource gl_resource = TransferableResource::MakeGL(
+      mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token1, size,
+      false /* is_overlay_candidate */);
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      gl_resource, SingleReleaseCallback::Create(base::DoNothing()));
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer some resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(1u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  unsigned parent_id = resource_map[list.front().id];
+
+  auto owned_image_context = std::make_unique<ExternalUseClient::ImageContext>(
+      gpu::MailboxHolder(mailbox, sync_token1, GL_TEXTURE_2D), size, RGBA_8888,
+      /*ycbcr_info=*/base::nullopt, /*color_space=*/nullptr);
+  auto* image_context = owned_image_context.get();
+
+  gpu::MailboxHolder holder;
+  EXPECT_CALL(client_, CreateImageContext(_, _, _, _, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&holder),
+                      Return(ByMove(std::move(owned_image_context)))));
+
+  ExternalUseClient::ImageContext* locked_image_context =
+      lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                              /*is_video_plane=*/false);
+  EXPECT_EQ(image_context, locked_image_context);
+  ASSERT_EQ(holder.mailbox, mailbox);
+  ASSERT_TRUE(holder.sync_token.HasData());
+
+  // Don't release while locked.
+  EXPECT_CALL(client_, ReleaseImageContexts(_)).Times(0);
+  // Return the resources back to the child. Nothing should happen because
+  // of the resource lock.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  // The resource should not be returned due to the external use lock.
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  // Disable access to gpu thread.
+  resource_provider_->SetAllowAccessToGPUThread(false);
+
+  gpu::SyncToken sync_token2(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x234),
+                             0x456);
+  sync_token2.SetVerifyFlush();
+
+  gpu::SyncToken sync_token3(gpu::CommandBufferNamespace::GPU_IO,
+                             gpu::CommandBufferId::FromUnsafeValue(0x234),
+                             0x567);
+  sync_token3.SetVerifyFlush();
+
+  // Without GPU thread access no ReleaseImageContexts() should happen
+  EXPECT_CALL(client_, ReleaseImageContexts(_)).Times(0);
+  // Unlock resources
+  lock_set_->UnlockResources(sync_token2);
+  // Resources should not be returned because we can't unlock them on GPU
+  // thread.
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  // We will get a second release of |parent_id| now that we've released our
+  // external lock and have access to GPU thread.
+  EXPECT_CALL(client_, ReleaseImageContexts(
+                           testing::ElementsAre(SamePtr(locked_image_context))))
+      .WillOnce(Return(sync_token3));
+  // Enable access to GPU Thread
+  resource_provider_->SetAllowAccessToGPUThread(true);
+
+  // The resource should be returned after the lock is released.
+  EXPECT_EQ(1u, returned_to_child.size());
+  EXPECT_EQ(sync_token3, returned_to_child[0].sync_token);
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  child_resource_provider_->RemoveImportedResource(id1);
+}
+
+class TestFence : public ResourceFence {
+ public:
+  TestFence() = default;
+
+  // ResourceFence implementation.
+  void Set() override {}
+  bool HasPassed() override { return passed; }
+
+  bool passed = false;
+
+ private:
+  ~TestFence() override = default;
+};
+
+TEST_F(DisplayResourceProviderSkiaTest,
+       ReadLockFenceStopsReturnToChildOrDelete) {
+  MockReleaseCallback release;
+  TransferableResource tran1 = CreateResource(RGBA_8888);
+  tran1.read_lock_fences_enabled = true;
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      tran1, SingleReleaseCallback::Create(base::BindOnce(
+                 &MockReleaseCallback::Released, base::Unretained(&release))));
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer some resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(1u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+  EXPECT_TRUE(list[0].read_lock_fences_enabled);
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  scoped_refptr<TestFence> fence(new TestFence);
+  resource_provider_->SetReadLockFence(fence.get());
+  {
+    unsigned parent_id = resource_map[list.front().id];
+    lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                            /*is_video_plane=*/false);
+    lock_set_->UnlockResources(GenSyncToken());
+  }
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  EXPECT_EQ(0u, returned_to_child.size());
+  fence->passed = true;
+
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  EXPECT_EQ(1u, returned_to_child.size());
+
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  EXPECT_CALL(release, Released(_, _));
+  child_resource_provider_->RemoveImportedResource(id1);
+}
+
+TEST_F(DisplayResourceProviderSkiaTest, ReadLockFenceDestroyChild) {
+  MockReleaseCallback release;
+
+  TransferableResource tran1 = CreateResource(RGBA_8888);
+  tran1.read_lock_fences_enabled = true;
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      tran1, SingleReleaseCallback::Create(base::BindOnce(
+                 &MockReleaseCallback::Released, base::Unretained(&release))));
+
+  TransferableResource tran2 = CreateResource(RGBA_8888);
+  tran2.read_lock_fences_enabled = false;
+  ResourceId id2 = child_resource_provider_->ImportResource(
+      tran2, SingleReleaseCallback::Create(base::BindOnce(
+                 &MockReleaseCallback::Released, base::Unretained(&release))));
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(2u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  scoped_refptr<TestFence> fence(new TestFence);
+  resource_provider_->SetReadLockFence(fence.get());
+  {
+    for (auto& resource : list) {
+      unsigned parent_id = resource_map[resource.id];
+      lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                              /*is_video_plane=*/false);
+    }
+    lock_set_->UnlockResources(GenSyncToken());
+  }
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+
+  resource_provider_->DestroyChild(child_id);
+
+  EXPECT_EQ(0u, resource_provider_->num_resources());
+  EXPECT_EQ(2u, returned_to_child.size());
+
+  // id1 should be lost and id2 should not.
+  EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
+  EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
+
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  EXPECT_CALL(release, Released(_, _)).Times(2);
+  child_resource_provider_->RemoveImportedResource(id1);
+  child_resource_provider_->RemoveImportedResource(id2);
+}
+
+TEST_F(DisplayResourceProviderSkiaTest, ReadLockFenceContextLost) {
+  TransferableResource tran1 = CreateResource(RGBA_8888);
+  tran1.read_lock_fences_enabled = true;
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      tran1, SingleReleaseCallback::Create(base::DoNothing()));
+
+  TransferableResource tran2 = CreateResource(RGBA_8888);
+  tran2.read_lock_fences_enabled = false;
+  ResourceId id2 = child_resource_provider_->ImportResource(
+      tran2, SingleReleaseCallback::Create(base::DoNothing()));
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(2u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  scoped_refptr<TestFence> fence(new TestFence);
+  resource_provider_->SetReadLockFence(fence.get());
+  {
+    for (auto& resource : list) {
+      unsigned parent_id = resource_map[resource.id];
+      DisplayResourceProvider::ScopedReadLockSharedImage lock(
+          resource_provider_.get(), parent_id);
+    }
+  }
+  EXPECT_EQ(0u, returned_to_child.size());
+
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+  resource_provider_->DidLoseContextProvider();
+  resource_provider_ = nullptr;
+
+  EXPECT_EQ(2u, returned_to_child.size());
+
+  EXPECT_TRUE(returned_to_child[0].lost);
+  EXPECT_TRUE(returned_to_child[1].lost);
+
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  child_resource_provider_->RemoveImportedResource(id1);
+  child_resource_provider_->RemoveImportedResource(id2);
+}
+
+// Test that ScopedBatchReturnResources batching works.
+TEST_F(DisplayResourceProviderSkiaTest,
+       ScopedBatchReturnResourcesPreventsReturn) {
+  MockReleaseCallback release;
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer some resources to the parent.
+  constexpr size_t kTotalResources = 5;
+  constexpr size_t kLockedResources = 3;
+  constexpr size_t kUsedResources = 4;
+  ResourceId ids[kTotalResources];
+  for (auto& id : ids) {
+    TransferableResource tran = CreateResource(RGBA_8888);
+    id = child_resource_provider_->ImportResource(
+        tran, SingleReleaseCallback::Create(base::BindOnce(
+                  &MockReleaseCallback::Released, base::Unretained(&release))));
+  }
+  std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources);
+
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      resource_ids_to_transfer, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(kTotalResources, list.size());
+  for (const auto& id : ids)
+    EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+  std::vector<
+      std::unique_ptr<DisplayResourceProvider::ScopedReadLockSharedImage>>
+      read_locks;
+  for (size_t i = 0; i < kLockedResources; i++) {
+    unsigned int mapped_resource_id = resource_map[ids[i]];
+    lock_set_->LockResource(mapped_resource_id, /*maybe_concurrent_reads=*/true,
+                            /*is_video_plane=*/false);
+  }
+
+  // Mark all locked resources, and one unlocked resource as used for first
+  // batch.
+  {
+    DisplayResourceProvider::ScopedBatchReturnResources returner(
+        resource_provider_.get());
+    resource_provider_->DeclareUsedResourcesFromChild(
+        child_id, ResourceIdSet(ids, ids + kUsedResources));
+    EXPECT_EQ(0u, returned_to_child.size());
+  }
+  EXPECT_EQ(1u, returned_to_child.size());
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  returned_to_child.clear();
+
+  // Return all locked resources.
+  {
+    DisplayResourceProvider::ScopedBatchReturnResources returner(
+        resource_provider_.get());
+    resource_provider_->DeclareUsedResourcesFromChild(
+        child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
+    // Can be called multiple times while batching is enabled.  This happens in
+    // practice when the same surface is visited using different paths during
+    // surface aggregation.
+    resource_provider_->DeclareUsedResourcesFromChild(
+        child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
+    lock_set_->UnlockResources(GenSyncToken());
+    EXPECT_EQ(0u, returned_to_child.size());
+  }
+  EXPECT_EQ(kLockedResources, returned_to_child.size());
+  // Returned resources that were locked share the same sync token.
+  for (const auto& resource : returned_to_child)
+    EXPECT_EQ(resource.sync_token, returned_to_child[0].sync_token);
+
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  returned_to_child.clear();
+
+  // Returns from destroying the child is also batched.
+  {
+    DisplayResourceProvider::ScopedBatchReturnResources returner(
+        resource_provider_.get());
+    resource_provider_->DestroyChild(child_id);
+    EXPECT_EQ(0u, returned_to_child.size());
+  }
+  EXPECT_EQ(1u, returned_to_child.size());
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+  returned_to_child.clear();
+
+  EXPECT_CALL(release, Released(_, _)).Times(kTotalResources);
+  for (const auto& id : ids)
+    child_resource_provider_->RemoveImportedResource(id);
+}  // namespace
+
+TEST_F(DisplayResourceProviderSkiaTest, LostMailboxInParent) {
+  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
+                            gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
+  auto tran = CreateResource(RGBA_8888);
+  tran.id = 11;
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id = resource_provider_->CreateChild(
+      base::BindRepeating(&CollectResources, &returned_to_child));
+
+  // Receive a resource then lose the gpu context.
+  resource_provider_->ReceiveFromChild(child_id, {tran});
+  resource_provider_->DidLoseContextProvider();
+
+  // Transfer resources back from the parent to the child.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, {});
+  ASSERT_EQ(1u, returned_to_child.size());
+
+  // Losing an output surface only loses hardware resources.
+  EXPECT_EQ(returned_to_child[0].lost, true);
+}
+
+#if defined(OS_ANDROID)
+TEST_F(DisplayResourceProviderSkiaTest, OverlayPromotionHint) {
+  gpu::Mailbox external_mailbox = gpu::Mailbox::GenerateForSharedImage();
+  gpu::SyncToken external_sync_token = GenSyncToken();
+  EXPECT_TRUE(external_sync_token.HasData());
+
+  TransferableResource id1_transfer = TransferableResource::MakeGL(
+      external_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, external_sync_token,
+      gfx::Size(1, 1), true);
+  id1_transfer.wants_promotion_hint = true;
+  id1_transfer.is_backed_by_surface_texture = true;
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      id1_transfer, SingleReleaseCallback::Create(base::DoNothing()));
+
+  TransferableResource id2_transfer = TransferableResource::MakeGL(
+      external_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, external_sync_token,
+      gfx::Size(1, 1), true);
+  id2_transfer.wants_promotion_hint = false;
+  id2_transfer.is_backed_by_surface_texture = false;
+  ResourceId id2 = child_resource_provider_->ImportResource(
+      id2_transfer, SingleReleaseCallback::Create(base::DoNothing()));
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id =
+      resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+
+  // Transfer some resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(2u, list.size());
+  resource_provider_->ReceiveFromChild(child_id, list);
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+  ResourceId mapped_id1 = resource_map[list[0].id];
+  ResourceId mapped_id2 = resource_map[list[1].id];
+
+  // The promotion hints should not be recorded until after we wait.  This is
+  // because we can't notify them until they're synchronized, and we choose to
+  // ignore unwaited resources rather than send them a "no" hint.  If they end
+  // up in the request set before we wait, then the attempt to notify them wil;
+  // DCHECK when we try to lock them for reading in SendPromotionHints.
+  EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
+  EXPECT_FALSE(resource_provider_->DoAnyResourcesWantPromotionHints());
+  {
+    resource_provider_->InitializePromotionHintRequest(mapped_id1);
+    DisplayResourceProvider::ScopedReadLockSharedImage lock(
+        resource_provider_.get(), mapped_id1);
+  }
+  EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());
+  EXPECT_TRUE(resource_provider_->DoAnyResourcesWantPromotionHints());
+
+  ResourceIdSet resource_ids_to_receive;
+  resource_ids_to_receive.insert(id1);
+  resource_ids_to_receive.insert(id2);
+  resource_provider_->DeclareUsedResourcesFromChild(child_id,
+                                                    resource_ids_to_receive);
+
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+
+  EXPECT_NE(0u, mapped_id1);
+  EXPECT_NE(0u, mapped_id2);
+
+  // Make sure that the request for a promotion hint was noticed.
+  EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id1));
+  EXPECT_TRUE(resource_provider_->IsBackedBySurfaceTexture(mapped_id1));
+  EXPECT_TRUE(resource_provider_->DoesResourceWantPromotionHint(mapped_id1));
+
+  EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id2));
+  EXPECT_FALSE(resource_provider_->IsBackedBySurfaceTexture(mapped_id2));
+  EXPECT_FALSE(resource_provider_->DoesResourceWantPromotionHint(mapped_id2));
+
+  // ResourceProvider maintains a set of promotion hint requests that should be
+  // cleared when resources are deleted.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  EXPECT_EQ(2u, returned_to_child.size());
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+
+  EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
+  EXPECT_FALSE(resource_provider_->DoAnyResourcesWantPromotionHints());
+
+  resource_provider_->DestroyChild(child_id);
+
+  child_resource_provider_->RemoveImportedResource(id2);
+  child_resource_provider_->RemoveImportedResource(id1);
+}
+#endif
+
+}  // namespace
+}  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_software.cc b/components/viz/service/display/display_resource_provider_software.cc
new file mode 100644
index 0000000..01147557
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_software.cc
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_resource_provider_software.h"
+
+namespace viz {
+
+DisplayResourceProviderSoftware::DisplayResourceProviderSoftware(
+    SharedBitmapManager* shared_bitmap_manager)
+    : DisplayResourceProvider(DisplayResourceProvider::kSoftware,
+                              /*compositor_context_provider=*/nullptr,
+                              shared_bitmap_manager,
+                              /*enable_shared_images=*/true) {}
+
+}  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_software.h b/components/viz/service/display/display_resource_provider_software.h
new file mode 100644
index 0000000..eb1ce88
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_software.h
@@ -0,0 +1,23 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SOFTWARE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SOFTWARE_H_
+
+#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// DisplayResourceProvider implementation used with SoftwareRenderer.
+class VIZ_SERVICE_EXPORT DisplayResourceProviderSoftware
+    : public DisplayResourceProvider {
+ public:
+  explicit DisplayResourceProviderSoftware(
+      SharedBitmapManager* shared_bitmap_manager);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SOFTWARE_H_
diff --git a/components/viz/service/display/display_resource_provider_software_unittest.cc b/components/viz/service/display/display_resource_provider_software_unittest.cc
new file mode 100644
index 0000000..8a966ac
--- /dev/null
+++ b/components/viz/service/display/display_resource_provider_software_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_resource_provider_software.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "build/build_config.h"
+#include "components/viz/client/client_resource_provider.h"
+#include "components/viz/common/resources/bitmap_allocation.h"
+#include "components/viz/common/resources/resource_format_utils.h"
+#include "components/viz/common/resources/returned_resource.h"
+#include "components/viz/common/resources/shared_bitmap.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
+#include "components/viz/test/test_shared_bitmap_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+namespace viz {
+namespace {
+
+class MockReleaseCallback {
+ public:
+  MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
+};
+
+static void CollectResources(std::vector<ReturnedResource>* array,
+                             const std::vector<ReturnedResource>& returned) {
+  array->insert(array->end(), returned.begin(), returned.end());
+}
+
+static SharedBitmapId CreateAndFillSharedBitmap(SharedBitmapManager* manager,
+                                                const gfx::Size& size,
+                                                ResourceFormat format,
+                                                uint32_t value) {
+  SharedBitmapId shared_bitmap_id = SharedBitmap::GenerateId();
+
+  base::MappedReadOnlyRegion shm =
+      bitmap_allocation::AllocateSharedBitmap(size, RGBA_8888);
+  manager->ChildAllocatedSharedBitmap(shm.region.Map(), shared_bitmap_id);
+  base::span<uint32_t> span =
+      shm.mapping.GetMemoryAsSpan<uint32_t>(size.GetArea());
+  std::fill(span.begin(), span.end(), value);
+  return shared_bitmap_id;
+}
+
+class DisplayResourceProviderSoftwareTest : public testing::Test {
+ public:
+  DisplayResourceProviderSoftwareTest() {
+    shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
+
+    resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>(
+        shared_bitmap_manager_.get());
+
+    child_resource_provider_ = std::make_unique<ClientResourceProvider>();
+  }
+
+  ~DisplayResourceProviderSoftwareTest() override {
+    child_resource_provider_->ShutdownAndReleaseAllResources();
+  }
+
+  TransferableResource CreateResource(ResourceFormat format) {
+    constexpr gfx::Size size(64, 64);
+    SharedBitmapId shared_bitmap_id = CreateAndFillSharedBitmap(
+        shared_bitmap_manager_.get(), size, format, 0);
+
+    return TransferableResource::MakeSoftware(shared_bitmap_id, size, format);
+  }
+
+ protected:
+  std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_;
+  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
+  std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
+};
+
+TEST_F(DisplayResourceProviderSoftwareTest, LostMailboxInParent) {
+  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
+                            gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
+  auto tran = CreateResource(RGBA_8888);
+  tran.id = 11;
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id = resource_provider_->CreateChild(
+      base::BindRepeating(&CollectResources, &returned_to_child));
+
+  // Receive a resource then lose the gpu context.
+  resource_provider_->ReceiveFromChild(child_id, {tran});
+  resource_provider_->DidLoseContextProvider();
+
+  // Transfer resources back from the parent to the child.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, {});
+  ASSERT_EQ(1u, returned_to_child.size());
+
+  // Losing an output surface only loses hardware resources.
+  EXPECT_EQ(returned_to_child[0].lost, false);
+}
+
+TEST_F(DisplayResourceProviderSoftwareTest, ReadSoftwareResources) {
+  gfx::Size size(64, 64);
+  ResourceFormat format = RGBA_8888;
+  const uint32_t kBadBeef = 0xbadbeef;
+  SharedBitmapId shared_bitmap_id = CreateAndFillSharedBitmap(
+      shared_bitmap_manager_.get(), size, format, kBadBeef);
+
+  auto resource =
+      TransferableResource::MakeSoftware(shared_bitmap_id, size, format);
+
+  MockReleaseCallback release;
+  ResourceId resource_id = child_resource_provider_->ImportResource(
+      resource,
+      SingleReleaseCallback::Create(base::BindOnce(
+          &MockReleaseCallback::Released, base::Unretained(&release))));
+  EXPECT_NE(0u, resource_id);
+
+  // Transfer resources to the parent.
+  std::vector<TransferableResource> send_to_parent;
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id = resource_provider_->CreateChild(
+      base::BindRepeating(&CollectResources, &returned_to_child));
+  child_resource_provider_->PrepareSendToParent(
+      {resource_id}, &send_to_parent,
+      static_cast<RasterContextProvider*>(nullptr));
+  resource_provider_->ReceiveFromChild(child_id, send_to_parent);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+  ResourceId mapped_resource_id = resource_map[resource_id];
+
+  {
+    DisplayResourceProviderSoftware::ScopedReadLockSkImage lock(
+        resource_provider_.get(), mapped_resource_id);
+    const SkImage* sk_image = lock.sk_image();
+    SkBitmap sk_bitmap;
+    sk_image->asLegacyBitmap(&sk_bitmap);
+    EXPECT_EQ(sk_image->width(), size.width());
+    EXPECT_EQ(sk_image->height(), size.height());
+    EXPECT_EQ(*sk_bitmap.getAddr32(16, 16), kBadBeef);
+  }
+
+  EXPECT_EQ(0u, returned_to_child.size());
+  // Transfer resources back from the parent to the child. Set no resources as
+  // being in use.
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+  EXPECT_EQ(1u, returned_to_child.size());
+  child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+
+  EXPECT_CALL(release, Released(_, false));
+  child_resource_provider_->RemoveImportedResource(resource_id);
+}
+
+}  // namespace
+}  // namespace viz
diff --git a/components/viz/service/display/display_scheduler_unittest.cc b/components/viz/service/display/display_scheduler_unittest.cc
index 4271f4c..1d18f5a 100644
--- a/components/viz/service/display/display_scheduler_unittest.cc
+++ b/components/viz/service/display/display_scheduler_unittest.cc
@@ -4,6 +4,10 @@
 
 #include "components/viz/service/display/display_scheduler.h"
 
+#include <set>
+#include <utility>
+#include <vector>
+
 #include "base/check.h"
 #include "base/stl_util.h"
 #include "base/test/null_task_runner.h"
@@ -13,6 +17,7 @@
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/service/display/display.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/fake_external_begin_frame_source.h"
@@ -154,10 +159,7 @@
       : fake_begin_frame_source_(0.f, false),
         task_runner_(new base::NullTaskRunner),
         surface_manager_(nullptr, 4u),
-        resource_provider_(DisplayResourceProvider::kSoftware,
-                           nullptr,
-                           &shared_bitmap_manager_,
-                           false),
+        resource_provider_(&shared_bitmap_manager_),
         aggregator_(&surface_manager_, &resource_provider_, false, false),
         damage_tracker_(
             std::make_unique<TestDisplayDamageTracker>(&surface_manager_,
@@ -216,7 +218,7 @@
   scoped_refptr<base::NullTaskRunner> task_runner_;
   SurfaceManager surface_manager_;
   ServerSharedBitmapManager shared_bitmap_manager_;
-  DisplayResourceProvider resource_provider_;
+  DisplayResourceProviderSoftware resource_provider_;
   SurfaceAggregator aggregator_;
   std::unique_ptr<TestDisplayDamageTracker> damage_tracker_;
   FakeDisplaySchedulerClient client_;
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 372bb83b..d2798eeb 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -34,7 +34,7 @@
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/platform_color.h"
 #include "components/viz/common/resources/transferable_resource.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 #include "components/viz/test/fake_output_surface.h"
 #include "components/viz/test/test_gles2_interface.h"
 #include "components/viz/test/test_shared_bitmap_manager.h"
@@ -621,7 +621,7 @@
   FakeRendererGL(const RendererSettings* settings,
                  const DebugRendererSettings* debug_settings,
                  OutputSurface* output_surface,
-                 DisplayResourceProvider* resource_provider)
+                 DisplayResourceProviderGL* resource_provider)
       : GLRenderer(settings,
                    debug_settings,
                    output_surface,
@@ -632,7 +632,7 @@
   FakeRendererGL(const RendererSettings* settings,
                  const DebugRendererSettings* debug_settings,
                  OutputSurface* output_surface,
-                 DisplayResourceProvider* resource_provider,
+                 DisplayResourceProviderGL* resource_provider,
                  OverlayProcessorInterface* overlay_processor)
       : GLRenderer(settings,
                    debug_settings,
@@ -645,7 +645,7 @@
       const RendererSettings* settings,
       const DebugRendererSettings* debug_settings,
       OutputSurface* output_surface,
-      DisplayResourceProvider* resource_provider,
+      DisplayResourceProviderGL* resource_provider,
       OverlayProcessorInterface* overlay_processor,
       scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
       : GLRenderer(settings,
@@ -668,9 +668,8 @@
     output_surface_->BindToClient(&output_surface_client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), shared_bitmap_manager_.get());
     renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
                                                  output_surface_.get(),
                                                  resource_provider_.get());
@@ -684,7 +683,7 @@
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<FakeRendererGL> renderer_;
 };
 
@@ -700,12 +699,11 @@
     output_surface_->BindToClient(&output_surface_client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        shared_bitmap_manager_.get());
-    renderer_.reset(new FakeRendererGL(&settings_, &debug_settings_,
-                                       output_surface_.get(),
-                                       resource_provider_.get(), nullptr));
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), shared_bitmap_manager_.get());
+    renderer_ = std::make_unique<FakeRendererGL>(
+        &settings_, &debug_settings_, output_surface_.get(),
+        resource_provider_.get(), nullptr);
     renderer_->Initialize();
     renderer_->SetVisible(true);
 
@@ -805,7 +803,7 @@
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   scoped_refptr<TestContextProvider> child_context_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<FakeRendererGL> renderer_;
@@ -1154,10 +1152,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1193,10 +1189,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1230,10 +1224,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1276,10 +1268,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1315,10 +1305,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1386,10 +1374,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1509,10 +1495,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1582,10 +1566,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   settings.should_clear_root_render_pass = false;
@@ -1692,10 +1674,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -1766,10 +1746,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   settings.partial_swap_enabled = true;
@@ -1980,10 +1958,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   {
     RendererSettings settings;
@@ -2028,9 +2004,8 @@
     output_surface_->BindToClient(&output_surface_client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), shared_bitmap_manager_.get());
     settings_.partial_swap_enabled = true;
     renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
                                                  output_surface_.get(),
@@ -2062,7 +2037,7 @@
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<FakeRendererGL> renderer_;
 };
 
@@ -2158,10 +2133,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
@@ -2532,9 +2505,8 @@
     output_surface_->BindToClient(&output_surface_client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), shared_bitmap_manager_.get());
 
     renderer_.reset(new FakeRendererGL(&settings_, &debug_settings_,
                                        output_surface_.get(),
@@ -2585,7 +2557,7 @@
   OutputSurfaceMockGLES2Interface* gl_ = nullptr;
   std::unique_ptr<StrictMock<MockOutputSurface>> output_surface_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<FakeRendererGL> renderer_;
 };
 
@@ -2765,9 +2737,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
-      DisplayResourceProvider::kGpu, output_surface->context_provider(),
-      shared_bitmap_manager.get());
+  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   auto child_context_provider = TestContextProvider::Create();
   child_context_provider->BindToCurrentThread();
@@ -2975,9 +2946,8 @@
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
       std::make_unique<TestSharedBitmapManager>();
-  auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
-      DisplayResourceProvider::kGpu, output_surface->context_provider(),
-      shared_bitmap_manager.get());
+  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), shared_bitmap_manager.get());
 
   auto child_context_provider = TestContextProvider::Create();
   child_context_provider->BindToCurrentThread();
@@ -3096,10 +3066,9 @@
       FakeOutputSurface::Create3d(std::move(provider)));
   cc::FakeOutputSurfaceClient output_surface_client;
   output_surface->BindToClient(&output_surface_client);
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          nullptr);
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider =
+      std::make_unique<DisplayResourceProviderGL>(
+          output_surface->context_provider(), nullptr);
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                           resource_provider.get());
@@ -3183,10 +3152,8 @@
       FakeOutputSurface::Create3d(std::move(provider)));
   cc::FakeOutputSurfaceClient output_surface_client;
   output_surface->BindToClient(&output_surface_client);
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          nullptr);
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), nullptr);
   RendererSettings settings;
   FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                           resource_provider.get());
@@ -3246,9 +3213,8 @@
     output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
     output_surface_->BindToClient(&output_surface_client_);
 
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        nullptr);
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), nullptr);
 
     settings_.partial_swap_enabled = true;
     settings_.slow_down_compositing_scale_factor = 1;
@@ -3330,7 +3296,7 @@
   FastSolidColorMockGLES2Interface* gl_ = nullptr;
   std::unique_ptr<FakeRendererGL> fake_renderer_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   cc::FakeOutputSurfaceClient output_surface_client_;
   RendererSettings settings_;
   base::test::ScopedFeatureList feature_list_;
@@ -3605,10 +3571,8 @@
     output_surface->set_supports_dc_layers(set_draw_rectangle);
     output_surface->BindToClient(&output_surface_client);
 
-    std::unique_ptr<DisplayResourceProvider> resource_provider =
-        std::make_unique<DisplayResourceProvider>(
-            DisplayResourceProvider::kGpu, output_surface->context_provider(),
-            nullptr);
+    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+        output_surface->context_provider(), nullptr);
 
     RendererSettings settings;
     settings.partial_swap_enabled = partial_swap;
@@ -3764,9 +3728,8 @@
   output_surface->set_supports_dc_layers(true);
   output_surface->BindToClient(&output_surface_client);
 
-  auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
-      DisplayResourceProvider::kGpu, output_surface->context_provider(),
-      nullptr);
+  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), nullptr);
 
   auto child_context_provider = TestContextProvider::Create();
   child_context_provider->BindToCurrentThread();
@@ -3888,9 +3851,8 @@
               gpu::ContextResult::kSuccess);
     output_surface_ = FakeOutputSurface::Create3d(std::move(context_provider));
     output_surface_->BindToClient(&output_surface_client_);
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        nullptr);
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), nullptr);
     renderer_ = std::make_unique<GLRenderer>(
         &settings_, &debug_settings_, output_surface_.get(),
         resource_provider_.get(), nullptr, nullptr);
@@ -3902,7 +3864,7 @@
   cc::FakeOutputSurfaceClient output_surface_client_;
   MockContextSupport* context_support_ptr_;
   std::unique_ptr<OutputSurface> output_surface_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<GLRenderer> renderer_;
 };
 
@@ -4019,10 +3981,8 @@
         FakeOutputSurface::Create3d(std::move(provider)));
     output_surface->BindToClient(&output_surface_client);
 
-    std::unique_ptr<DisplayResourceProvider> resource_provider =
-        std::make_unique<DisplayResourceProvider>(
-            DisplayResourceProvider::kGpu, output_surface->context_provider(),
-            nullptr);
+    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+        output_surface->context_provider(), nullptr);
 
     RendererSettings settings;
     auto processor =
@@ -4111,9 +4071,8 @@
     output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
     output_surface_->BindToClient(&output_surface_client);
 
-    display_resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        nullptr);
+    display_resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), nullptr);
 
     settings_ = std::make_unique<RendererSettings>();
     // This setting is enabled to use CALayer overlays.
@@ -4160,7 +4119,7 @@
  private:
   MockCALayerGLES2Interface* gl_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
-  std::unique_ptr<DisplayResourceProvider> display_resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> display_resource_provider_;
   std::unique_ptr<RendererSettings> settings_;
   std::unique_ptr<OverlayProcessorInterface> overlay_processor_;
   std::unique_ptr<FakeRendererGL> renderer_;
@@ -5018,7 +4977,7 @@
   FramebufferWatchingGLRenderer(RendererSettings* settings,
                                 const DebugRendererSettings* debug_settings,
                                 OutputSurface* output_surface,
-                                DisplayResourceProvider* resource_provider)
+                                DisplayResourceProviderGL* resource_provider)
       : FakeRendererGL(settings,
                        debug_settings,
                        output_surface,
@@ -5060,10 +5019,8 @@
   auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
   output_surface->BindToClient(&output_surface_client);
 
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(
-          DisplayResourceProvider::kGpu, output_surface->context_provider(),
-          nullptr);
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      output_surface->context_provider(), nullptr);
 
   for (int i = 0; i < 2; ++i) {
     bool use_partial_swap = i == 0;
@@ -5162,9 +5119,8 @@
     output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
     output_surface_->set_overlay_texture_id(kSurfaceOverlayTextureId);
     output_surface_->set_gpu_fence_id(kGpuFenceId);
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, output_surface_->context_provider(),
-        nullptr);
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        output_surface_->context_provider(), nullptr);
     overlay_processor_ = std::make_unique<SingleOverlayOnTopProcessor>();
     overlay_processor_->AllowMultipleCandidates();
     renderer_ = std::make_unique<FakeRendererGL>(
@@ -5210,7 +5166,7 @@
 
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   scoped_refptr<TestContextProvider> child_context_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   RendererSettings settings_;
diff --git a/components/viz/service/display/null_renderer.cc b/components/viz/service/display/null_renderer.cc
new file mode 100644
index 0000000..7534c40
--- /dev/null
+++ b/components/viz/service/display/null_renderer.cc
@@ -0,0 +1,56 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/null_renderer.h"
+
+#include "base/notreached.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/service/display/output_surface.h"
+
+namespace viz {
+
+NullRenderer::NullRenderer(const RendererSettings* settings,
+                           const DebugRendererSettings* debug_settings,
+                           OutputSurface* output_surface,
+                           DisplayResourceProvider* resource_provider,
+                           OverlayProcessorInterface* overlay_processor)
+    : DirectRenderer(settings,
+                     debug_settings,
+                     output_surface,
+                     resource_provider,
+                     overlay_processor) {
+  DCHECK(output_surface->capabilities().skips_draw);
+}
+NullRenderer::~NullRenderer() = default;
+
+void NullRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
+  NOTREACHED();
+}
+void NullRenderer::BeginDrawingFrame() {
+  NOTREACHED();
+}
+
+bool NullRenderer::CanPartialSwap() {
+  return false;
+}
+
+bool NullRenderer::IsRenderPassResourceAllocated(
+    const AggregatedRenderPassId& render_pass_id) const {
+  return false;
+}
+
+gfx::Size NullRenderer::GetRenderPassBackingPixelSize(
+    const AggregatedRenderPassId& render_pass_id) {
+  return gfx::Size();
+}
+
+bool NullRenderer::FlippedFramebuffer() const {
+  return false;
+}
+
+void NullRenderer::CopyDrawnRenderPass(
+    const copy_output::RenderPassGeometry& geometry,
+    std::unique_ptr<CopyOutputRequest> request) {}
+
+}  // namespace viz
diff --git a/components/viz/service/display/null_renderer.h b/components/viz/service/display/null_renderer.h
new file mode 100644
index 0000000..fa291a47a
--- /dev/null
+++ b/components/viz/service/display/null_renderer.h
@@ -0,0 +1,61 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_NULL_RENDERER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_NULL_RENDERER_H_
+
+#include <memory>
+
+#include "components/viz/service/display/direct_renderer.h"
+
+namespace viz {
+
+// Empty implementation of the DirectRenderer, used with OutputSurfaceUnified.
+// Doesn't support Draw and will crash if Draw of SwapBuffers will be called.
+class VIZ_SERVICE_EXPORT NullRenderer : public DirectRenderer {
+ public:
+  NullRenderer(const RendererSettings* settings,
+               const DebugRendererSettings* debug_settings,
+               OutputSurface* output_surface,
+               DisplayResourceProvider* resource_provider,
+               OverlayProcessorInterface* overlay_processor);
+  ~NullRenderer() override;
+
+ private:
+  void SwapBuffers(SwapFrameData swap_frame_data) override;
+  bool CanPartialSwap() override;
+  void UpdateRenderPassTextures(
+      const AggregatedRenderPassList& render_passes_in_draw_order,
+      const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
+          render_passes_in_frame) override {}
+  void AllocateRenderPassResourceIfNeeded(
+      const AggregatedRenderPassId& render_pass_id,
+      const RenderPassRequirements& requirements) override {}
+  bool IsRenderPassResourceAllocated(
+      const AggregatedRenderPassId& render_pass_id) const override;
+  gfx::Size GetRenderPassBackingPixelSize(
+      const AggregatedRenderPassId& render_pass_id) override;
+  void BindFramebufferToOutputSurface() override {}
+  void BindFramebufferToTexture(
+      const AggregatedRenderPassId render_pass_id) override {}
+  void SetScissorTestRect(const gfx::Rect& scissor_rect) override {}
+  void PrepareSurfaceForPass(SurfaceInitializationMode initialization_mode,
+                             const gfx::Rect& render_pass_scissor) override {}
+  void DoDrawQuad(const DrawQuad* quad,
+                  const gfx::QuadF* clip_region) override {}
+  void BeginDrawingFrame() override;
+  void FlushOverdrawFeedback(const gfx::Rect& output_rect) override {}
+  void FinishDrawingFrame() override {}
+  bool FlippedFramebuffer() const override;
+  void EnsureScissorTestEnabled() override {}
+  void EnsureScissorTestDisabled() override {}
+  void DidChangeVisibility() override {}
+  void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry,
+                           std::unique_ptr<CopyOutputRequest> request) override;
+  void GenerateMipmap() override {}
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_NULL_RENDERER_H_
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc
index 537f8f6..cdeec342 100644
--- a/components/viz/service/display/overlay_ca_unittest.cc
+++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -26,7 +26,7 @@
 #include "components/viz/common/quads/video_hole_draw_quad.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 #include "components/viz/service/display/gl_renderer.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/output_surface_client.h"
@@ -219,9 +219,8 @@
     output_surface_->BindToClient(&client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, provider_.get(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        provider_.get(), shared_bitmap_manager_.get());
 
     child_provider_ = TestContextProvider::Create();
     child_provider_->BindToCurrentThread();
@@ -245,7 +244,7 @@
   std::unique_ptr<OverlayOutputSurface> output_surface_;
   cc::FakeOutputSurfaceClient client_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   scoped_refptr<TestContextProvider> child_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<CATestOverlayProcessor> overlay_processor_;
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc
index f392032..d44d13c 100644
--- a/components/viz/service/display/overlay_dc_unittest.cc
+++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -25,7 +25,7 @@
 #include "components/viz/common/quads/video_hole_draw_quad.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "components/viz/service/display/dc_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 #include "components/viz/service/display/gl_renderer.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/output_surface_client.h"
@@ -232,9 +232,8 @@
     output_surface_->BindToClient(&client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, provider_.get(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        provider_.get(), shared_bitmap_manager_.get());
 
     child_provider_ = TestContextProvider::Create();
     child_provider_->BindToCurrentThread();
@@ -262,7 +261,7 @@
   std::unique_ptr<OverlayOutputSurface> output_surface_;
   cc::FakeOutputSurfaceClient client_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   scoped_refptr<TestContextProvider> child_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<OverlayProcessorWin> overlay_processor_;
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 1e3c5c5..e4526913 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -27,7 +27,7 @@
 #include "components/viz/common/quads/video_hole_draw_quad.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 #include "components/viz/service/display/gl_renderer.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/output_surface_client.h"
@@ -598,9 +598,8 @@
     output_surface_->BindToClient(&client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, provider_.get(),
-        shared_bitmap_manager_.get());
+    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
+        provider_.get(), shared_bitmap_manager_.get());
 
     child_provider_ = TestContextProvider::Create();
     child_provider_->BindToCurrentThread();
@@ -659,10 +658,8 @@
   output_surface.BindToClient(&client);
 
   auto shared_bitmap_manager = std::make_unique<TestSharedBitmapManager>();
-  std::unique_ptr<DisplayResourceProvider> resource_provider =
-      std::make_unique<DisplayResourceProvider>(DisplayResourceProvider::kGpu,
-                                                provider.get(),
-                                                shared_bitmap_manager.get());
+  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
+      provider.get(), shared_bitmap_manager.get());
 
   auto overlay_processor = std::make_unique<TestOverlayProcessor>();
   EXPECT_GE(2U, overlay_processor->GetStrategyCount());
@@ -3061,7 +3058,7 @@
   OverlayInfoRendererGL(const RendererSettings* settings,
                         const DebugRendererSettings* debug_settings,
                         OutputSurface* output_surface,
-                        DisplayResourceProvider* resource_provider,
+                        DisplayResourceProviderGL* resource_provider,
                         SingleOverlayProcessor* overlay_processor)
       : GLRenderer(settings,
                    debug_settings,
@@ -3126,8 +3123,8 @@
     provider_->BindToCurrentThread();
     output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
     output_surface_->BindToClient(&output_surface_client_);
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kGpu, provider_.get(), nullptr);
+    resource_provider_ =
+        std::make_unique<DisplayResourceProviderGL>(provider_.get(), nullptr);
 
     provider_->support()->SetScheduleOverlayPlaneCallback(base::BindRepeating(
         &MockOverlayScheduler::Schedule, base::Unretained(&scheduler_)));
@@ -3174,8 +3171,8 @@
   void SwapBuffersWithoutComplete() { renderer_->SwapBuffers({}); }
   void SwapBuffersComplete() { renderer_->SwapBuffersComplete(); }
   void ReturnResourceInUseQuery(ResourceId id) {
-    DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
-                                                   id);
+    DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
+                                                     id);
     gpu::TextureInUseResponse response;
     response.texture = lock.texture_id();
     response.in_use = false;
@@ -3212,7 +3209,7 @@
   DebugRendererSettings debug_settings_;
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<OverlayOutputSurface> output_surface_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
   std::unique_ptr<SingleOverlayProcessor> owned_overlay_processor_;
   std::unique_ptr<OverlayInfoRendererGL> renderer_;
   scoped_refptr<TestContextProvider> provider_;
diff --git a/components/viz/service/display/software_renderer_unittest.cc b/components/viz/service/display/software_renderer_unittest.cc
index 17966bc..24401d99 100644
--- a/components/viz/service/display/software_renderer_unittest.cc
+++ b/components/viz/service/display/software_renderer_unittest.cc
@@ -30,7 +30,7 @@
 #include "components/viz/common/quads/tile_draw_quad.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
 #include "components/viz/common/resources/shared_bitmap.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/software_output_device.h"
 #include "components/viz/test/fake_output_surface.h"
 #include "components/viz/test/test_shared_bitmap_manager.h"
@@ -52,8 +52,7 @@
     output_surface_->BindToClient(&output_surface_client_);
 
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kSoftware, nullptr,
+    resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>(
         shared_bitmap_manager_.get());
     renderer_ = std::make_unique<SoftwareRenderer>(
         &settings_, &debug_settings_, output_surface_.get(),
@@ -70,7 +69,7 @@
     child_resource_provider_ = nullptr;
   }
 
-  DisplayResourceProvider* resource_provider() const {
+  DisplayResourceProviderSoftware* resource_provider() const {
     return resource_provider_.get();
   }
 
@@ -133,7 +132,7 @@
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<FakeOutputSurface> output_surface_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
-  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<SoftwareRenderer> renderer_;
 };
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 0683a78..f1fee4ad 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -1541,7 +1541,8 @@
 
   // Ref the resources in the surface, and let the provider know we've received
   // new resources from the compositor frame.
-  surface->RefResources(resource_list);
+  if (surface->client())
+    surface->client()->RefResources(resource_list);
   provider_->ReceiveFromChild(child_id, resource_list);
 
   // Figure out which resources are actually used in the render pass.
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 35c0402..753d7474 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -8,7 +8,7 @@
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/service/display/aggregated_frame.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/surface_aggregator.h"
 #include "components/viz/service/display/viz_perf_test.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
@@ -16,7 +16,6 @@
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "components/viz/test/compositor_frame_helpers.h"
-#include "components/viz/test/test_context_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_result_reporter.h"
 
@@ -56,11 +55,7 @@
 class SurfaceAggregatorPerfTest : public VizPerfTest {
  public:
   SurfaceAggregatorPerfTest() : manager_(&shared_bitmap_manager_) {
-    context_provider_ = TestContextProvider::Create();
-    context_provider_->BindToCurrentThread();
-
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kSoftware, context_provider_.get(),
+    resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>(
         &shared_bitmap_manager_);
   }
 
@@ -198,7 +193,6 @@
  protected:
   ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
-  scoped_refptr<TestContextProvider> context_provider_;
   std::unique_ptr<DisplayResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
 };
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 2dcab904..649e347 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -34,7 +34,7 @@
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
-#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -5582,8 +5582,8 @@
   SurfaceAggregatorWithResourcesTest() : manager_(&shared_bitmap_manager_) {}
 
   void SetUp() override {
-    resource_provider_ = std::make_unique<DisplayResourceProvider>(
-        DisplayResourceProvider::kSoftware, nullptr, &shared_bitmap_manager_);
+    resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>(
+        &shared_bitmap_manager_);
 
     aggregator_ = std::make_unique<SurfaceAggregator>(
         manager_.surface_manager(), resource_provider_.get(), false, false);
diff --git a/components/viz/service/display_embedder/output_surface_unified.h b/components/viz/service/display_embedder/output_surface_unified.h
index 26e07e39..1fa3e08 100644
--- a/components/viz/service/display_embedder/output_surface_unified.h
+++ b/components/viz/service/display_embedder/output_surface_unified.h
@@ -15,10 +15,10 @@
 // issue begin frames and doesn't need to do any drawing work. This class is
 // essentially a stub implementation.
 //
-// OutputSurfaceUnified will end up with a corresponding SoftwareRenderer. While
+// OutputSurfaceUnified will end up with a corresponding NullRenderer. While
 // Chrome OS uses GL rendering to draw it doesn't matter what renderer is
 // created for the unified display because it's never used to draw. Using
-// SoftwareRenderer avoids the need to allocate a GL context and command buffer,
+// NullRenderer avoids the need to allocate a GL context and command buffer,
 // which have significant memory overhead.
 class OutputSurfaceUnified : public OutputSurface {
  public:
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index e8d9048..535cbd55 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -47,8 +47,14 @@
 
 }  // namespace
 
-SkiaOutputDevice::ScopedPaint::ScopedPaint(SkiaOutputDevice* device)
-    : device_(device), sk_surface_(device->BeginPaint(&end_semaphores_)) {}
+SkiaOutputDevice::ScopedPaint::ScopedPaint(
+    std::vector<GrBackendSemaphore> end_semaphores,
+    SkiaOutputDevice* device,
+    SkSurface* sk_surface)
+    : end_semaphores_(std::move(end_semaphores)),
+      device_(device),
+      sk_surface_(sk_surface) {}
+
 SkiaOutputDevice::ScopedPaint::~ScopedPaint() {
   DCHECK(end_semaphores_.empty());
   device_->EndPaint();
@@ -99,6 +105,17 @@
     latency_tracker_runner_->DeleteSoon(FROM_HERE, std::move(latency_tracker_));
 }
 
+std::unique_ptr<SkiaOutputDevice::ScopedPaint>
+SkiaOutputDevice::BeginScopedPaint() {
+  std::vector<GrBackendSemaphore> end_semaphores;
+  SkSurface* sk_surface = BeginPaint(&end_semaphores);
+  if (!sk_surface) {
+    return nullptr;
+  }
+  return std::make_unique<SkiaOutputDevice::ScopedPaint>(
+      std::move(end_semaphores), this, sk_surface);
+}
+
 void SkiaOutputDevice::Submit(bool sync_cpu, base::OnceClosure callback) {
   gr_context_->submit(sync_cpu);
   std::move(callback).Run();
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index 7fade68..7cd93285 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -54,7 +54,9 @@
   // A helper class for defining a BeginPaint() and EndPaint() scope.
   class ScopedPaint {
    public:
-    explicit ScopedPaint(SkiaOutputDevice* device);
+    ScopedPaint(std::vector<GrBackendSemaphore> end_semaphores,
+                SkiaOutputDevice* device,
+                SkSurface* sk_surface);
     ~ScopedPaint();
 
     // This can be null.
@@ -94,6 +96,12 @@
       DidSwapBufferCompleteCallback did_swap_buffer_complete_callback);
   virtual ~SkiaOutputDevice();
 
+  // Begins a paint scope. The base implementation fails when the SkSurface
+  // cannot be initialized, but devices that don't draw to a SkSurface (i.e
+  // |SkiaOutputDeviceVulkanSecondaryCB|) can override this to bypass the
+  // check.
+  virtual std::unique_ptr<SkiaOutputDevice::ScopedPaint> BeginScopedPaint();
+
   // Changes the size of draw surface and invalidates it's contents.
   virtual bool Reshape(const gfx::Size& size,
                        float device_scale_factor,
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
index b70072f..d3bdfd9 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
@@ -48,6 +48,14 @@
       sk_color_type;
 }
 
+std::unique_ptr<SkiaOutputDevice::ScopedPaint>
+SkiaOutputDeviceVulkanSecondaryCB::BeginScopedPaint() {
+  std::vector<GrBackendSemaphore> end_semaphores;
+  SkSurface* sk_surface = BeginPaint(&end_semaphores);
+  return std::make_unique<SkiaOutputDevice::ScopedPaint>(
+      std::move(end_semaphores), this, sk_surface);
+}
+
 void SkiaOutputDeviceVulkanSecondaryCB::Submit(bool sync_cpu,
                                                base::OnceClosure callback) {
   // Submit the primary command buffer which may render passes.
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h
index 8207a3b2..97fce81 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h
@@ -21,6 +21,7 @@
       gpu::MemoryTracker* memory_tracker,
       DidSwapBufferCompleteCallback did_swap_buffer_complete_callback);
 
+  std::unique_ptr<SkiaOutputDevice::ScopedPaint> BeginScopedPaint() override;
   void Submit(bool sync_cpu, base::OnceClosure callback) override;
   bool Reshape(const gfx::Size& size,
                float device_scale_factor,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index cee5b54a..8967aa8 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -545,8 +545,11 @@
   // We do not reset scoped_output_device_paint_ after drawing the ddl until
   // SwapBuffers() is called, because we may need access to output_sk_surface()
   // for CopyOutput().
-  scoped_output_device_paint_.emplace(output_device_.get());
-  DCHECK(scoped_output_device_paint_);
+  scoped_output_device_paint_ = output_device_->BeginScopedPaint();
+  if (!scoped_output_device_paint_) {
+    MarkContextLost(ContextLostReason::CONTEXT_LOST_BEGIN_PAINT_FAILED);
+    return;
+  }
 
   dependency_->ScheduleGrContextCleanup();
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index e7b6f8a..aadf50d 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -333,7 +333,7 @@
   base::flat_set<ImageContextImpl*> image_contexts_with_end_access_state_;
 
   std::unique_ptr<SkiaOutputDevice> output_device_;
-  base::Optional<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_;
+  std::unique_ptr<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_;
 
   base::Optional<OverlayProcessorInterface::OutputSurfaceOverlayPlane>
       output_surface_plane_;
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 3224499..20fdb2b 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -127,16 +127,6 @@
   previous_frame_surface_id_ = surface->surface_id();
 }
 
-void Surface::RefResources(const std::vector<TransferableResource>& resources) {
-  if (surface_client_)
-    surface_client_->RefResources(resources);
-}
-
-void Surface::UnrefResources(const std::vector<ReturnedResource>& resources) {
-  if (surface_client_)
-    surface_client_->UnrefResources(resources);
-}
-
 void Surface::UpdateSurfaceReferences() {
   const base::flat_set<SurfaceId>& existing_referenced_surfaces =
       surface_manager_->GetSurfacesReferencedByParent(surface_id());
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index eedc4afa..97eb7e4 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -128,12 +128,6 @@
 
   void SetPreviousFrameSurface(Surface* surface);
 
-  // Increments the reference count on resources specified by |resources|.
-  void RefResources(const std::vector<TransferableResource>& resources);
-
-  // Decrements the reference count on resources specified by |resources|.
-  void UnrefResources(const std::vector<ReturnedResource>& resources);
-
   // Returns false if |frame| is invalid.
   // |frame_rejected_callback| will be called once if the frame will not be
   // displayed.
diff --git a/components/webapps/services/web_app_origin_association/BUILD.gn b/components/webapps/services/web_app_origin_association/BUILD.gn
index 8f5be8b1..150c9087 100644
--- a/components/webapps/services/web_app_origin_association/BUILD.gn
+++ b/components/webapps/services/web_app_origin_association/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("lib") {
   sources = [
+    "web_app_origin_association_fetcher.cc",
+    "web_app_origin_association_fetcher.h",
     "web_app_origin_association_parser.cc",
     "web_app_origin_association_parser.h",
     "web_app_origin_association_parser_impl.cc",
@@ -12,7 +14,12 @@
 
   deps = [
     "//base",
+    "//components/services/app_service/public/cpp:app_url_handling",
     "//mojo/public/cpp/bindings",
+    "//net/traffic_annotation:traffic_annotation",
+    "//services/network/public/cpp",
+    "//skia",
+    "//url",
   ]
 
   public_deps = [
@@ -37,6 +44,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "web_app_origin_association_fetcher_unittest.cc",
     "web_app_origin_association_parser_impl_unittest.cc",
     "web_app_origin_association_parser_unittest.cc",
   ]
@@ -45,6 +53,13 @@
     "//base",
     "//base/test:test_support",
     "//components/webapps/services/web_app_origin_association:lib",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//net",
+    "//net:test_support",
+    "//services/network:network_service",
+    "//services/network:test_support",
+    "//skia",
     "//testing/gtest",
     "//url",
   ]
diff --git a/components/webapps/services/web_app_origin_association/DEPS b/components/webapps/services/web_app_origin_association/DEPS
index e841ea2..9601bfc 100644
--- a/components/webapps/services/web_app_origin_association/DEPS
+++ b/components/webapps/services/web_app_origin_association/DEPS
@@ -1,4 +1,9 @@
 include_rules = [
+  "+components/services/app_service/public/cpp",
   "+content/public/browser",
+  "+content/public/test",
   "+mojo/public",
+  "+net",
+  "+services/network",
+  "+third_party/skia/include",
 ]
diff --git a/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.cc b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.cc
new file mode 100644
index 0000000..a636d8c6
--- /dev/null
+++ b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.cc
@@ -0,0 +1,116 @@
+// Copyright 2021 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/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr size_t kMaxJsonSize = 1000000;  // 1MB max
+
+int g_max_retry = 3;
+
+network::SimpleURLLoader::RetryMode g_retry_mode =
+    network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE;
+
+constexpr net::NetworkTrafficAnnotationTag
+    web_app_origin_association_traffic_annotation =
+        net::DefineNetworkTrafficAnnotation(
+            "web_app_origin_association_download",
+            R"(
+      semantics {
+          sender: "Web App Origin Association Fetcher"
+          description:
+            "PWAs can specify URL Handlers in the Manifest. To verify the "
+            "handlers, we download the corresponding web app origin "
+            "association files."
+          trigger:
+            "A PWA that has URL Handlers declared in the Manifest is "
+            "installed, updated, or when DevTools displays URL Handler "
+            "information to users."
+          data:
+            "Nothing."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+          "There is no setting to disable PWA installation."
+          policy_exception_justification:
+            "Not implemented, "
+            "considered not necessary as no user data is sent."
+    })");
+
+constexpr char association_file_name[] =
+    ".well-known/web-app-origin-association";
+
+std::unique_ptr<network::SimpleURLLoader> CreateRequester(const GURL& url) {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->method = "GET";
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request),
+      web_app_origin_association_traffic_annotation);
+  url_loader->SetRetryOptions(g_max_retry, g_retry_mode);
+  url_loader->SetURLLoaderFactoryOptions(
+      network::mojom::kURLLoadOptionBlockAllCookies);
+  return url_loader;
+}
+}  // namespace
+
+namespace webapps {
+
+WebAppOriginAssociationFetcher::WebAppOriginAssociationFetcher() = default;
+
+WebAppOriginAssociationFetcher::~WebAppOriginAssociationFetcher() = default;
+
+void WebAppOriginAssociationFetcher::SetRetryOptionsForTest(
+    int max_retry,
+    network::SimpleURLLoader::RetryMode retry_mode) {
+  g_max_retry = max_retry;
+  g_retry_mode = retry_mode;
+}
+
+void WebAppOriginAssociationFetcher::FetchWebAppOriginAssociationFile(
+    const apps::UrlHandlerInfo& url_handler,
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
+    FetchFileCallback callback) {
+  const GURL resource_url =
+      url_handler.origin.GetURL().Resolve(association_file_name);
+  if (!resource_url.is_valid() || resource_url.is_empty()) {
+    // Do not proceed if |resource_url| is not valid.
+    OnResponse(std::move(callback), nullptr);
+    return;
+  }
+
+  SendRequest(resource_url, std::move(shared_url_loader_factory),
+              std::move(callback));
+}
+
+void WebAppOriginAssociationFetcher::SendRequest(
+    const GURL& url,
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
+    FetchFileCallback callback) {
+  url_loader_ = CreateRequester(url);
+  url_loader_->DownloadToString(
+      shared_url_loader_factory.get(),
+      base::BindOnce(&WebAppOriginAssociationFetcher::OnResponse,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
+      kMaxJsonSize);
+}
+
+void WebAppOriginAssociationFetcher::OnResponse(
+    FetchFileCallback callback,
+    std::unique_ptr<std::string> response_body) {
+  std::move(callback).Run(std::move(response_body));
+}
+
+}  // namespace webapps
diff --git a/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.h b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.h
new file mode 100644
index 0000000..4b5b6cef
--- /dev/null
+++ b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.h
@@ -0,0 +1,61 @@
+// Copyright 2021 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_WEBAPPS_SERVICES_WEB_APP_ORIGIN_ASSOCIATION_WEB_APP_ORIGIN_ASSOCIATION_FETCHER_H_
+#define COMPONENTS_WEBAPPS_SERVICES_WEB_APP_ORIGIN_ASSOCIATION_WEB_APP_ORIGIN_ASSOCIATION_FETCHER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/services/app_service/public/cpp/url_handler_info.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+class GURL;
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace webapps {
+
+using FetchFileCallback =
+    base::OnceCallback<void(std::unique_ptr<std::string> file_content)>;
+
+// Makes network requests to fetch web app origin association files.
+class WebAppOriginAssociationFetcher {
+ public:
+  WebAppOriginAssociationFetcher();
+  ~WebAppOriginAssociationFetcher();
+  WebAppOriginAssociationFetcher(const WebAppOriginAssociationFetcher&) =
+      delete;
+  WebAppOriginAssociationFetcher& operator=(
+      const WebAppOriginAssociationFetcher&) = delete;
+
+  void FetchWebAppOriginAssociationFile(
+      const apps::UrlHandlerInfo& url_handler,
+      scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
+      FetchFileCallback callback);
+
+  void SetRetryOptionsForTest(int max_retry,
+                              network::SimpleURLLoader::RetryMode retry_mode);
+
+ private:
+  void SendRequest(
+      const GURL& url,
+      scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
+      FetchFileCallback callback);
+  void OnResponse(FetchFileCallback callback,
+                  std::unique_ptr<std::string> response_body);
+
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+  base::WeakPtrFactory<WebAppOriginAssociationFetcher> weak_ptr_factory_{this};
+};
+
+}  // namespace webapps
+
+#endif  // COMPONENTS_WEBAPPS_SERVICES_WEB_APP_ORIGIN_ASSOCIATION_WEB_APP_ORIGIN_ASSOCIATION_FETCHER_H_
diff --git a/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher_unittest.cc b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher_unittest.cc
new file mode 100644
index 0000000..bef3a0c
--- /dev/null
+++ b/components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright 2021 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/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.h"
+
+#include <string>
+#include <utility>
+
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "services/network/network_service.h"
+#include "services/network/test/test_network_context_client.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kWebAppOriginAssociationFileContent[] =
+    R"({\"web_apps\": [{"
+    "    \"manifest\": \"https://foo.com/manifest.json\","
+    "    \"details\": {"
+    "      \"paths\": [\"/*\"],"
+    "      \"exclude_paths\": [\"/blog/data\"]"
+    "    }"
+    "}]})";
+
+}  // namespace
+
+namespace webapps {
+
+class WebAppOriginAssociationFetcherTest : public testing::Test {
+ public:
+  WebAppOriginAssociationFetcherTest()
+      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+        server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    // Make sure the Network Service is started before making a NetworkContext.
+    content::GetNetworkService();
+
+    shared_url_loader_factory_ =
+        base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
+            network::NetworkService::GetNetworkServiceForTesting());
+
+    fetcher_ = std::make_unique<WebAppOriginAssociationFetcher>();
+
+    // Do not retry, otherwise TestSharedURLLoaderFactory.Clone() will be
+    // called, which is not implemented.
+    fetcher_->SetRetryOptionsForTest(0, network::SimpleURLLoader::RETRY_NEVER);
+  }
+
+  void SetUp() override {
+    server_.RegisterRequestHandler(
+        base::BindRepeating(&WebAppOriginAssociationFetcherTest::HandleRequest,
+                            base::Unretained(this)));
+
+    ASSERT_TRUE(test_server_handle_ = server_.StartAndReturnHandle());
+  }
+
+ protected:
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    if (request.relative_url != "/.well-known/web-app-origin-association")
+      return nullptr;
+
+    auto http_response =
+        std::make_unique<net::test_server::BasicHttpResponse>();
+    http_response->set_code(net::HTTP_OK);
+    http_response->set_content_type("application/json");
+    http_response->set_content(kWebAppOriginAssociationFileContent);
+    return http_response;
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  net::EmbeddedTestServer server_;
+  net::test_server::EmbeddedTestServerHandle test_server_handle_;
+  scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory_;
+  std::unique_ptr<WebAppOriginAssociationFetcher> fetcher_;
+};
+
+TEST_F(WebAppOriginAssociationFetcherTest, FileExists) {
+  base::RunLoop run_loop;
+  auto handler = apps::UrlHandlerInfo();
+  handler.origin = url::Origin::Create(GURL(server_.base_url()));
+  fetcher_->FetchWebAppOriginAssociationFile(
+      std::move(handler), shared_url_loader_factory_.get(),
+      base::BindLambdaForTesting(
+          [&](std::unique_ptr<std::string> file_content) {
+            ASSERT_FALSE(!file_content);
+            EXPECT_EQ(*file_content, kWebAppOriginAssociationFileContent);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
+TEST_F(WebAppOriginAssociationFetcherTest, FileDoesNotExist) {
+  base::RunLoop run_loop;
+  auto handler = apps::UrlHandlerInfo();
+  handler.origin = url::Origin::Create(server_.GetURL("https://foo.com", "/"));
+  fetcher_->FetchWebAppOriginAssociationFile(
+      std::move(handler), shared_url_loader_factory_.get(),
+      base::BindLambdaForTesting(
+          [&](std::unique_ptr<std::string> file_content) {
+            ASSERT_TRUE(!file_content);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
+}  // namespace webapps
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 41e70a3..d099977 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2714,7 +2714,6 @@
       "//chromeos/dbus/power:power_manager_proto",
       "//chromeos/network",
       "//chromeos/system",
-      "//chromeos/tpm",
       "//components/session_manager/core",
     ]
   }
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index fd2f595..64b80367 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -271,6 +271,12 @@
     ExpectOutcome(
         BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
         location);
+    ExpectNotRestoredReasons(reasons, location);
+  }
+
+  void ExpectNotRestoredReasons(
+      std::vector<BackForwardCacheMetrics::NotRestoredReason> reasons,
+      base::Location location) {
     uint64_t not_restored_reasons_bits = 0;
     for (BackForwardCacheMetrics::NotRestoredReason reason : reasons) {
       base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
@@ -538,10 +544,10 @@
     // If we restored the page, there should be no blocking reasons logged.
     if (outcome ==
         BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored) {
+      ExpectNotRestoredReasons({}, location);
       ExpectBlocklistedFeatures({}, FROM_HERE);
       ExpectDisabledWithReasons({}, FROM_HERE);
       ExpectBrowsingInstanceNotSwappedReasons({}, FROM_HERE);
-      ExpectBrowsingInstanceNotSwappedReasons({}, FROM_HERE);
     }
     if (!check_all_sites_)
       return;
diff --git a/content/browser/direct_sockets/direct_sockets_browsertest.cc b/content/browser/direct_sockets/direct_sockets_browsertest.cc
index 2488161..437c825 100644
--- a/content/browser/direct_sockets/direct_sockets_browsertest.cc
+++ b/content/browser/direct_sockets/direct_sockets_browsertest.cc
@@ -38,6 +38,7 @@
 #include "services/network/test/test_network_context.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "url/gurl.h"
+#include "url/url_canon_ip.h"
 
 using testing::StartsWith;
 
@@ -630,27 +631,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest,
-                       OpenTcp_RestrictedByEnterprisePolicies) {
-  EXPECT_TRUE(NavigateToURL(shell(), GetTestPageURL()));
-
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 0);
-
-  DirectSocketsServiceImpl::SetEnterpriseManagedForTesting(true);
-
-  const std::string script =
-      "openTcp({remoteAddress: '127.0.0.1', remotePort: 993})";
-
-  EXPECT_EQ("openTcp failed: NotAllowedError: Permission denied",
-            EvalJs(shell(), script));
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest,
                        OpenTcp_CannotConnectNonPublic) {
   const auto protocol = DirectSocketsServiceImpl::ProtocolType::kTcp;
   // Tests for the reserved IPv4 ranges. The reserved ranges are tested by
@@ -880,27 +860,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest,
-                       OpenUdp_RestrictedByEnterprisePolicies) {
-  EXPECT_TRUE(NavigateToURL(shell(), GetTestPageURL()));
-
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 0);
-
-  DirectSocketsServiceImpl::SetEnterpriseManagedForTesting(true);
-
-  const std::string script =
-      "openUdp({remoteAddress: '127.0.0.1', remotePort: 993})";
-
-  EXPECT_EQ("openUdp failed: NotAllowedError: Permission denied",
-            EvalJs(shell(), script));
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(DirectSocketsBrowserTest,
                        OpenUdp_CannotConnectNonPublic) {
   const auto protocol = DirectSocketsServiceImpl::ProtocolType::kUdp;
   // Tests for the reserved IPv4 ranges. The reserved ranges are tested by
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc
index c6970c5..7ffff22 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.cc
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -10,7 +10,6 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
-#include "build/build_config.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/browser/browser_thread.h"
@@ -24,18 +23,10 @@
 #include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
-#if defined(OS_WIN) || defined(OS_MAC)
-#include "base/enterprise_util.h"
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/tpm/install_attributes.h"
-#endif
-
 namespace content {
 
 namespace {
 
-base::Optional<bool> g_is_enterprise_managed_for_testing;
-
 constexpr int32_t kMaxBufferSize = 32 * 1024 * 1024;
 
 constexpr char kPermissionDeniedHistogramName[] =
@@ -82,23 +73,6 @@
   return false;
 }
 
-// TODO(crbug.com/1119662): Now only check for the device, maybe there are some
-// methods that can be applied to check for the user profile.
-bool IsEnterpriseManaged() {
-  // Return the value of the testing flag if it's set.
-  if (g_is_enterprise_managed_for_testing.has_value())
-    return g_is_enterprise_managed_for_testing.value();
-
-#if defined(OS_WIN) || defined(OS_MAC)
-  return base::IsMachineExternallyManaged();
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
-  return chromeos::InstallAttributes::IsInitialized() &&
-         chromeos::InstallAttributes::Get()->IsEnterpriseManaged();
-#else
-  return false;
-#endif
-}
-
 }  // namespace
 
 DirectSocketsServiceImpl::DirectSocketsServiceImpl(RenderFrameHost& frame_host)
@@ -333,12 +307,6 @@
 }
 
 // static
-void DirectSocketsServiceImpl::SetEnterpriseManagedForTesting(
-    bool enterprise_managed) {
-  g_is_enterprise_managed_for_testing = enterprise_managed;
-}
-
-// static
 void DirectSocketsServiceImpl::SetPermissionCallbackForTesting(
     PermissionCallback callback) {
   GetPermissionCallbackForTesting() = std::move(callback);
@@ -395,14 +363,7 @@
   if (options.send_buffer_size < 0 || options.receive_buffer_size < 0)
     return net::ERR_INVALID_ARGUMENT;
 
-  // By default, we will restrict use of the API when enterprise software
-  // policies are in effect.
-  if (IsEnterpriseManaged()) {
-    base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  FailureType::kEnterprisePolicy);
-    return net::ERR_NETWORK_ACCESS_DENIED;
-  }
-
+  // TODO(crbug.com/1119662): Check for enterprise software policies.
   // TODO(crbug.com/1119659): Check permissions policy.
   // TODO(crbug.com/1119600): Implement rate limiting.
 
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.h b/content/browser/direct_sockets/direct_sockets_service_impl.h
index dc37332..1ffa586 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.h
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.h
@@ -74,8 +74,6 @@
   void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
   void WebContentsDestroyed() override;
 
-  static void SetEnterpriseManagedForTesting(bool enterprise_managed);
-
   static void SetPermissionCallbackForTesting(PermissionCallback callback);
 
   static void SetNetworkContextForTesting(network::mojom::NetworkContext*);
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index a15a6e1..01d39c6 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -277,6 +277,7 @@
 #endif
 #if defined(OS_MAC)
     sandbox::policy::switches::kEnableSandboxLogging,
+    sandbox::policy::switches::kDisableMetalShaderCache,
     switches::kDisableAVFoundationOverlays,
     switches::kDisableMacOverlays,
     switches::kDisableMetalTestShaders,
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc
index 1ab49fc3..93c89f7 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -38,17 +38,7 @@
 
 IndexedDBInternalsUI::IndexedDBInternalsUI(WebUI* web_ui)
     : WebUIController(web_ui) {
-  web_ui->RegisterMessageCallback(
-      "getAllOrigins", base::BindRepeating(&IndexedDBInternalsUI::GetAllOrigins,
-                                           base::Unretained(this)));
-
-  web_ui->RegisterMessageCallback(
-      "downloadOriginData",
-      base::BindRepeating(&IndexedDBInternalsUI::DownloadOriginData,
-                          base::Unretained(this)));
-  web_ui->RegisterMessageCallback(
-      "forceClose", base::BindRepeating(&IndexedDBInternalsUI::ForceCloseOrigin,
-                                        base::Unretained(this)));
+  web_ui->AddMessageHandler(std::make_unique<IndexedDBInternalsHandler>());
   WebUIDataSource* source =
       WebUIDataSource::Create(kChromeUIIndexedDBInternalsHost);
   source->OverrideContentSecurityPolicy(
@@ -69,42 +59,66 @@
   WebUIDataSource::Add(browser_context, source);
 }
 
-IndexedDBInternalsUI::~IndexedDBInternalsUI() {}
+IndexedDBInternalsUI::~IndexedDBInternalsUI() = default;
 
-void IndexedDBInternalsUI::GetAllOrigins(const base::ListValue* args) {
+IndexedDBInternalsHandler::IndexedDBInternalsHandler() = default;
+
+IndexedDBInternalsHandler::~IndexedDBInternalsHandler() = default;
+
+void IndexedDBInternalsHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "getAllOrigins",
+      base::BindRepeating(&IndexedDBInternalsHandler::GetAllOrigins,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      "downloadOriginData",
+      base::BindRepeating(&IndexedDBInternalsHandler::DownloadOriginData,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "forceClose",
+      base::BindRepeating(&IndexedDBInternalsHandler::ForceCloseOrigin,
+                          base::Unretained(this)));
+}
+
+void IndexedDBInternalsHandler::OnJavascriptDisallowed() {
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+void IndexedDBInternalsHandler::GetAllOrigins(const base::ListValue* args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  AllowJavascript();
   BrowserContext* browser_context =
       web_ui()->GetWebContents()->GetBrowserContext();
 
   BrowserContext::ForEachStoragePartition(
       browser_context,
       base::BindRepeating(
-          [](base::WeakPtr<IndexedDBInternalsUI> ui,
+          [](base::WeakPtr<IndexedDBInternalsHandler> handler,
              StoragePartition* partition) {
-            if (!ui)
+            if (!handler)
               return;
             auto& control = partition->GetIndexedDBControl();
             control.GetAllOriginsDetails(base::BindOnce(
-                [](base::WeakPtr<IndexedDBInternalsUI> ui,
+                [](base::WeakPtr<IndexedDBInternalsHandler> handler,
                    base::FilePath partition_path, bool incognito,
                    base::Value info_list) {
-                  if (!ui)
+                  if (!handler)
                     return;
 
-                  ui->OnOriginsReady(
+                  handler->OnOriginsReady(
                       info_list, incognito ? base::FilePath() : partition_path);
                 },
-                ui, partition->GetPath()));
+                handler, partition->GetPath()));
           },
           weak_factory_.GetWeakPtr()));
 }
 
-void IndexedDBInternalsUI::OnOriginsReady(const base::Value& origins,
-                                          const base::FilePath& path) {
+void IndexedDBInternalsHandler::OnOriginsReady(const base::Value& origins,
+                                               const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  web_ui()->CallJavascriptFunctionUnsafe("indexeddb.onOriginsReady", origins,
-                                         base::Value(path.AsUTF8Unsafe()));
+  FireWebUIListener("origins-ready", origins, base::Value(path.AsUTF8Unsafe()));
 }
 
 static void FindControl(const base::FilePath& partition_path,
@@ -117,18 +131,25 @@
   }
 }
 
-bool IndexedDBInternalsUI::GetOriginData(
+bool IndexedDBInternalsHandler::GetOriginData(
     const base::ListValue* args,
+    std::string* callback_id,
     base::FilePath* partition_path,
     Origin* origin,
     storage::mojom::IndexedDBControl** control) {
+  std::string callback_string;
+  if (!args->GetString(0, &callback_string)) {
+    return false;
+  }
+  *callback_id = callback_string;
+
   std::string path_string;
-  if (!args->GetString(0, &path_string))
+  if (!args->GetString(1, &path_string))
     return false;
   *partition_path = base::FilePath::FromUTF8Unsafe(path_string);
 
   std::string url_string;
-  if (!args->GetString(1, &url_string))
+  if (!args->GetString(2, &url_string))
     return false;
 
   *origin = Origin::Create(GURL(url_string));
@@ -136,7 +157,7 @@
   return GetOriginControl(*partition_path, *origin, control);
 }
 
-bool IndexedDBInternalsUI::GetOriginControl(
+bool IndexedDBInternalsHandler::GetOriginControl(
     const base::FilePath& path,
     const Origin& origin,
     storage::mojom::IndexedDBControl** control) {
@@ -156,88 +177,92 @@
   return true;
 }
 
-void IndexedDBInternalsUI::DownloadOriginData(const base::ListValue* args) {
+void IndexedDBInternalsHandler::DownloadOriginData(
+    const base::ListValue* args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  std::string callback_id;
   base::FilePath partition_path;
   Origin origin;
   storage::mojom::IndexedDBControl* control;
-  if (!GetOriginData(args, &partition_path, &origin, &control))
+  if (!GetOriginData(args, &callback_id, &partition_path, &origin, &control))
     return;
 
+  AllowJavascript();
   DCHECK(control);
   control->ForceClose(
       origin, storage::mojom::ForceCloseReason::FORCE_CLOSE_INTERNALS_PAGE,
       base::BindOnce(
-          [](base::WeakPtr<IndexedDBInternalsUI> ui, Origin origin,
-             base::FilePath partition_path,
-             storage::mojom::IndexedDBControl* control) {
+          [](base::WeakPtr<IndexedDBInternalsHandler> handler, Origin origin,
+             storage::mojom::IndexedDBControl* control,
+             const std::string& callback_id) {
             // Is the connection count always zero after closing,
             // such that this can be simplified?
             control->GetConnectionCount(
                 origin,
                 base::BindOnce(
-                    [](base::WeakPtr<IndexedDBInternalsUI> ui, Origin origin,
-                       base::FilePath partition_path,
-                       storage::mojom::IndexedDBControl* control,
+                    [](base::WeakPtr<IndexedDBInternalsHandler> handler,
+                       Origin origin, storage::mojom::IndexedDBControl* control,
+                       const std::string& callback_id,
                        uint64_t connection_count) {
-                      if (!ui)
+                      if (!handler)
                         return;
 
                       control->DownloadOriginData(
                           origin,
                           base::BindOnce(
-                              &IndexedDBInternalsUI::OnDownloadDataReady, ui,
-                              partition_path, origin, connection_count));
+                              &IndexedDBInternalsHandler::OnDownloadDataReady,
+                              handler, callback_id, connection_count));
                     },
-                    ui, origin, partition_path, control));
+                    handler, origin, control, callback_id));
           },
-          weak_factory_.GetWeakPtr(), origin, partition_path, control));
+          weak_factory_.GetWeakPtr(), origin, control, callback_id));
 }
 
-void IndexedDBInternalsUI::ForceCloseOrigin(const base::ListValue* args) {
+void IndexedDBInternalsHandler::ForceCloseOrigin(const base::ListValue* args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  std::string callback_id;
   base::FilePath partition_path;
   Origin origin;
   storage::mojom::IndexedDBControl* control;
-  if (!GetOriginData(args, &partition_path, &origin, &control))
+  if (!GetOriginData(args, &callback_id, &partition_path, &origin, &control))
     return;
 
+  AllowJavascript();
   control->ForceClose(
       origin, storage::mojom::ForceCloseReason::FORCE_CLOSE_INTERNALS_PAGE,
       base::BindOnce(
-          [](base::WeakPtr<IndexedDBInternalsUI> ui,
-             base::FilePath partition_path, Origin origin,
-             storage::mojom::IndexedDBControl* control) {
-            if (!ui)
+          [](base::WeakPtr<IndexedDBInternalsHandler> handler, Origin origin,
+             storage::mojom::IndexedDBControl* control,
+             const std::string& callback_id) {
+            if (!handler)
               return;
             control->GetConnectionCount(
-                origin, base::BindOnce(&IndexedDBInternalsUI::OnForcedClose, ui,
-                                       partition_path, origin));
+                origin,
+                base::BindOnce(&IndexedDBInternalsHandler::OnForcedClose,
+                               handler, callback_id));
           },
-          weak_factory_.GetWeakPtr(), partition_path, origin, control));
+          weak_factory_.GetWeakPtr(), origin, control, callback_id));
 }
 
-void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path,
-                                         const Origin& origin,
-                                         uint64_t connection_count) {
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "indexeddb.onForcedClose", base::Value(partition_path.AsUTF8Unsafe()),
-      base::Value(origin.Serialize()),
-      base::Value(static_cast<double>(connection_count)));
+void IndexedDBInternalsHandler::OnForcedClose(const std::string& callback_id,
+                                              uint64_t connection_count) {
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(static_cast<double>(connection_count)));
 }
 
-void IndexedDBInternalsUI::OnDownloadDataReady(
-    const base::FilePath& partition_path,
-    const Origin& origin,
+void IndexedDBInternalsHandler::OnDownloadDataReady(
+    const std::string& callback_id,
     uint64_t connection_count,
     bool success,
     const base::FilePath& temp_path,
     const base::FilePath& zip_path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!success)
+  if (!success) {
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
+  }
 
   const GURL url = GURL("file://" + zip_path.AsUTF8Unsafe());
   WebContents* web_contents = web_ui()->GetWebContents();
@@ -275,8 +300,8 @@
   // to start, then attach a download::DownloadItem::Observer to observe the
   // state change to the finished state.
   dl_params->set_callback(base::BindOnce(
-      &IndexedDBInternalsUI::OnDownloadStarted, base::Unretained(this),
-      partition_path, origin, temp_path, connection_count));
+      &IndexedDBInternalsHandler::OnDownloadStarted, base::Unretained(this),
+      temp_path, callback_id, connection_count));
 
   BrowserContext* context = web_contents->GetBrowserContext();
   BrowserContext::GetDownloadManager(context)->DownloadUrl(
@@ -326,25 +351,22 @@
                      std::move(temp_dir_)));
 }
 
-void IndexedDBInternalsUI::OnDownloadStarted(
-    const base::FilePath& partition_path,
-    const Origin& origin,
+void IndexedDBInternalsHandler::OnDownloadStarted(
     const base::FilePath& temp_path,
+    const std::string& callback_id,
     size_t connection_count,
     download::DownloadItem* item,
     download::DownloadInterruptReason interrupt_reason) {
   if (interrupt_reason != download::DOWNLOAD_INTERRUPT_REASON_NONE) {
     LOG(ERROR) << "Error downloading database dump: "
                << DownloadInterruptReasonToString(interrupt_reason);
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
 
   item->AddObserver(new FileDeleter(temp_path));
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "indexeddb.onOriginDownloadReady",
-      base::Value(partition_path.AsUTF8Unsafe()),
-      base::Value(origin.Serialize()),
-      base::Value(static_cast<double>(connection_count)));
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(static_cast<double>(connection_count)));
 }
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.h b/content/browser/indexed_db/indexed_db_internals_ui.h
index 22b9bf3..4c38d1c 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.h
+++ b/content/browser/indexed_db/indexed_db_internals_ui.h
@@ -15,6 +15,7 @@
 #include "components/download/public/common/download_interrupt_reasons.h"
 #include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
 class ListValue;
@@ -37,38 +38,49 @@
   ~IndexedDBInternalsUI() override;
 
  private:
+  base::WeakPtrFactory<IndexedDBInternalsUI> weak_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBInternalsUI);
+};
+
+class IndexedDBInternalsHandler : public WebUIMessageHandler {
+ public:
+  IndexedDBInternalsHandler();
+  ~IndexedDBInternalsHandler() override;
+
+  // WebUIMessageHandler implementation.
+  void RegisterMessages() override;
+  void OnJavascriptDisallowed() override;
+
+ private:
   void GetAllOrigins(const base::ListValue* args);
   void OnOriginsReady(const base::Value& origins, const base::FilePath& path);
 
   void DownloadOriginData(const base::ListValue* args);
-  void OnDownloadDataReady(const base::FilePath& partition_path,
-                           const url::Origin& origin,
+  void OnDownloadDataReady(const std::string& callback_id,
                            uint64_t connection_count,
                            bool success,
                            const base::FilePath& temp_path,
                            const base::FilePath& zip_path);
-  void OnDownloadStarted(const base::FilePath& partition_path,
-                         const url::Origin& origin,
-                         const base::FilePath& temp_path,
+  void OnDownloadStarted(const base::FilePath& temp_path,
+                         const std::string& callback_id,
                          size_t connection_count,
                          download::DownloadItem* item,
                          download::DownloadInterruptReason interrupt_reason);
 
   void ForceCloseOrigin(const base::ListValue* args);
-  void OnForcedClose(const base::FilePath& partition_path,
-                     const url::Origin& origin,
-                     uint64_t connection_count);
+  void OnForcedClose(const std::string& callback_id, uint64_t connection_count);
 
   bool GetOriginControl(const base::FilePath& path,
                         const url::Origin& origin,
                         storage::mojom::IndexedDBControl** control);
   bool GetOriginData(const base::ListValue* args,
+                     std::string* callback_id,
                      base::FilePath* path,
                      url::Origin* origin,
                      storage::mojom::IndexedDBControl** control);
 
-  base::WeakPtrFactory<IndexedDBInternalsUI> weak_factory_{this};
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBInternalsUI);
+  base::WeakPtrFactory<IndexedDBInternalsHandler> weak_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(IndexedDBInternalsHandler);
 };
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_controller_unittest.cc b/content/browser/media/session/media_session_controller_unittest.cc
index b4a7e1a..674239b 100644
--- a/content/browser/media/session/media_session_controller_unittest.cc
+++ b/content/browser/media/session/media_session_controller_unittest.cc
@@ -178,7 +178,7 @@
   }
 
   IPC::TestSink& test_sink() {
-    return main_test_rfh()->agent_scheduling_group().sink();
+    return main_test_rfh()->GetAgentSchedulingGroup().sink();
   }
 
   void Suspend() {
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 2073dfb..199fb60 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -22,6 +22,7 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/browser_url_handler_impl.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/network_service_instance_impl.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/content_navigation_policy.h"
@@ -42,6 +43,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/network_service_util.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -61,6 +63,7 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/did_commit_navigation_interceptor.h"
 #include "content/test/fake_network_url_loader_factory.h"
+#include "content/test/task_runner_deferring_throttle.h"
 #include "content/test/test_content_browser_client.h"
 #include "content/test/test_render_frame_host_factory.h"
 #include "ipc/ipc_security_test_util.h"
@@ -4150,6 +4153,109 @@
       current_frame_host()->GetLastCommittedOrigin().CanBeDerivedFrom(url));
 }
 
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
+                       ProcessShutdownDuringDeferredNavigationThrottle) {
+  GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  class ShutdownThrottle : public TaskRunnerDeferringThrottle,
+                           WebContentsObserver {
+   public:
+    explicit ShutdownThrottle(WebContents* web_contents,
+                              NavigationHandle* handle)
+        : TaskRunnerDeferringThrottle(base::ThreadTaskRunnerHandle::Get(),
+                                      /*defer_start=*/false,
+                                      /*defer_redirect=*/false,
+                                      /*defer_response=*/true,
+                                      handle),
+          web_contents_(web_contents) {
+      WebContentsObserver::Observe(web_contents_);
+    }
+
+    void AsyncResume() override {
+      // Shutdown the renderer and delay Resume() until then.
+      web_contents_->GetMainFrame()->GetProcess()->Shutdown(1);
+    }
+
+    void RenderFrameDeleted(RenderFrameHost* frame_host) override {
+      TaskRunnerDeferringThrottle::AsyncResume();
+    }
+
+   private:
+    WebContents* web_contents_;
+  };
+
+  auto inserter = std::make_unique<TestNavigationThrottleInserter>(
+      shell()->web_contents(),
+      base::BindLambdaForTesting(
+          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
+            return std::make_unique<ShutdownThrottle>(shell()->web_contents(),
+                                                      handle);
+          }));
+
+  class DoesNotReadyToCommitObserver : public WebContentsObserver {
+   public:
+    explicit DoesNotReadyToCommitObserver(WebContents* contents)
+        : WebContentsObserver(contents) {}
+
+    // WebContentsObserver overrides.
+    void ReadyToCommitNavigation(NavigationHandle* handle) override {
+      // This method should not happen. Since the process is destroyed before
+      // we become ready to commit, we can not ever reach
+      // ReadyToCommitNavigation. Doing so would fail because the renderer is
+      // gone.
+      ADD_FAILURE() << "ReadyToCommitNavigation but renderer has crashed. "
+                       "IsRenderFrameLive: "
+                    << handle->GetRenderFrameHost()->IsRenderFrameLive();
+      navigation_was_ready_to_commit_ = true;
+    }
+
+    void DidFinishNavigation(NavigationHandle* handle) override {
+      navigation_finished_ = true;
+      navigation_committed_ = handle->HasCommitted();
+    }
+
+    bool navigation_was_ready_to_commit() {
+      return navigation_was_ready_to_commit_;
+    }
+    bool navigation_finished() { return navigation_finished_; }
+    bool navigation_committed() { return navigation_committed_; }
+
+   private:
+    bool navigation_was_ready_to_commit_ = false;
+    bool navigation_finished_ = false;
+    bool navigation_committed_ = false;
+  };
+
+  // Watch that ReadyToCommitNavigation() will not happen when the renderer is
+  // gone.
+  DoesNotReadyToCommitObserver no_commit_obs(shell()->web_contents());
+
+  // We will shutdown the renderer during this navigation.
+  ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
+  // Important: This is a browser-initiated navigation, so the NavigationRequest
+  // does not have an open connection (NavigationClient) to the renderer that it
+  // is listening to for termination while running NavigationThrottles.
+  //
+  // Expect this navigation to be aborted, so we stop waiting after the
+  // uncommitted navigation is done.
+  GURL url2 = embedded_test_server()->GetURL("a.com", "/title1.html");
+  NavigateToURLBlockUntilNavigationsComplete(
+      shell(), url2, /*number_of_navigations=*/1,
+      /*ignore_uncommitted_navigations=*/false);
+
+  // The renderer was shutdown mid-navigation.
+  EXPECT_FALSE(shell()->web_contents()->GetMainFrame()->IsRenderFrameLive());
+
+  // The navigation was aborted, which means it finished but did not commit, and
+  // _importantly_ it never reported "ReadyToCommitNavigation" without a live
+  // renderer.
+  EXPECT_TRUE(no_commit_obs.navigation_finished());
+  EXPECT_FALSE(no_commit_obs.navigation_was_ready_to_commit());
+  EXPECT_FALSE(no_commit_obs.navigation_committed());
+}
+
 // Do sandbox flags apply to error page in sandboxed iframes?
 // Apparently yes.
 // TODO(https://crbug.com/1158370): Reconsider this.
@@ -4326,6 +4432,47 @@
   console_observer.Wait();
 }
 
+namespace {
+
+void VerifyResultsOfAboutBlankNavigation(RenderFrameHostImpl* target_frame,
+                                         RenderFrameHostImpl* initiator_frame) {
+  // Verify that `target_frame` has been navigated to "about:blank".
+  EXPECT_EQ(GURL(url::kAboutBlankURL), target_frame->GetLastCommittedURL());
+
+  // Verify that "about:blank" committed with the expected origin, and in the
+  // expected SiteInstance.
+  EXPECT_EQ(target_frame->GetLastCommittedOrigin(),
+            initiator_frame->GetLastCommittedOrigin());
+  EXPECT_EQ(target_frame->GetSiteInstance(),
+            initiator_frame->GetSiteInstance());
+
+  // Start monitoring NetworkService for crashes.
+  //
+  // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
+  // (with optional opt-out for things like NetworkServiceRestartBrowserTest).
+  bool did_network_service_crash = false;
+  base::CallbackListSubscription crash_monitoring_subscription =
+      RegisterNetworkServiceCrashHandler(base::BindLambdaForTesting(
+          [&]() { did_network_service_crash = true; }));
+  // Ask for cookies in the `target_frame`.  One implicit verification here
+  // is whether this step will hit any `cookie_url`-related NOTREACHED or DwoC
+  // in RestrictedCookieManager::ValidateAccessToCookiesAt.  This verification
+  // is non-racey, because `document.cookie` must have heard back from the
+  // RestrictedCookieManager before returning the value of cookies (this ignores
+  // possible Blink-side caching, but this is the first time the renderer needs
+  // the cookies and so this is okay for this test).
+  EXPECT_EQ("", EvalJs(target_frame, "document.cookie"));
+  // |network_context| might receive an error notification, but it's not
+  // guaranteed to have arrived at this point. Flush the remote to make sure
+  // the notification has been received.
+  // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
+  if (!IsInProcessNetworkService())
+    target_frame->FlushNetworkAndNavigationInterfacesForTesting();
+  EXPECT_FALSE(did_network_service_crash);
+}
+
+}  // namespace
+
 // The test below verifies that an "about:blank" navigation commits with the
 // right origin, even when the initiator of the navigation is not the parent or
 // opener of the frame targeted by the navigation.  In the
@@ -4372,10 +4519,7 @@
       shell()->web_contents()->GetMainFrame());
   child_frame = main_frame->child_at(0)->current_frame_host();
   grandchild_frame = child_frame->child_at(0)->current_frame_host();
-  EXPECT_EQ(main_frame->GetLastCommittedOrigin(),
-            grandchild_frame->GetLastCommittedOrigin());
-  EXPECT_EQ(GURL(url::kAboutBlankURL), grandchild_frame->GetLastCommittedURL());
-  EXPECT_EQ(main_frame->GetSiteInstance(), grandchild_frame->GetSiteInstance());
+  VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
 }
 
 // The test below verifies that an "about:blank" navigation commits with the
@@ -4425,10 +4569,7 @@
       shell()->web_contents()->GetMainFrame());
   child_frame = main_frame->child_at(0)->current_frame_host();
   grandchild_frame = child_frame->child_at(0)->current_frame_host();
-  EXPECT_EQ(main_frame->GetLastCommittedOrigin(),
-            grandchild_frame->GetLastCommittedOrigin());
-  EXPECT_EQ(GURL(url::kAboutBlankURL), grandchild_frame->GetLastCommittedURL());
-  EXPECT_EQ(main_frame->GetSiteInstance(), grandchild_frame->GetSiteInstance());
+  VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
 }
 
 // The test below verifies that an "about:blank" navigation commits with the
@@ -4479,10 +4620,7 @@
       shell()->web_contents()->GetMainFrame());
   child_frame = main_frame->child_at(0)->current_frame_host();
   grandchild_frame = child_frame->child_at(0)->current_frame_host();
-  EXPECT_EQ(main_frame->GetLastCommittedOrigin(),
-            grandchild_frame->GetLastCommittedOrigin());
-  EXPECT_EQ(GURL(url::kAboutBlankURL), grandchild_frame->GetLastCommittedURL());
-  EXPECT_EQ(main_frame->GetSiteInstance(), grandchild_frame->GetSiteInstance());
+  VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
 }
 
 // The test below verifies that an "about:blank" navigation commits with the
@@ -4570,10 +4708,7 @@
       shell()->web_contents()->GetMainFrame());
   child_frame1 = main_frame->child_at(0)->current_frame_host();
   child_frame2 = main_frame->child_at(1)->current_frame_host();
-  EXPECT_EQ(GURL(url::kAboutBlankURL), child_frame2->GetLastCommittedURL());
-  EXPECT_EQ(child_frame1->GetLastCommittedOrigin(),
-            child_frame2->GetLastCommittedOrigin());
-  EXPECT_EQ(child_frame1->GetSiteInstance(), child_frame2->GetSiteInstance());
+  VerifyResultsOfAboutBlankNavigation(child_frame2, child_frame1);
 }
 
 }  // namespace content
diff --git a/content/browser/process_internals/process_internals_handler_impl.cc b/content/browser/process_internals/process_internals_handler_impl.cc
index 5eed173..c0aa1121 100644
--- a/content/browser/process_internals/process_internals_handler_impl.cc
+++ b/content/browser/process_internals/process_internals_handler_impl.cc
@@ -34,7 +34,7 @@
 
   frame_info->routing_id = frame->GetRoutingID();
   frame_info->agent_scheduling_group_id =
-      frame->agent_scheduling_group().id_for_debugging();
+      frame->GetAgentSchedulingGroup().id_for_debugging();
   frame_info->process_id = frame->GetProcess()->GetID();
   frame_info->last_committed_url =
       frame->GetLastCommittedURL().is_valid()
diff --git a/content/browser/renderer_host/frame_tree_browsertest.cc b/content/browser/renderer_host/frame_tree_browsertest.cc
index b514bf9..845fe61 100644
--- a/content/browser/renderer_host/frame_tree_browsertest.cc
+++ b/content/browser/renderer_host/frame_tree_browsertest.cc
@@ -383,9 +383,11 @@
       "    resolve(frames[0].self.origin);"
       "  }, 16);"
       "});");
+  // Since we used document.write(), the URL of the frame document changes to
+  // match the document that called it.
+  EXPECT_EQ(initiator->current_url(), target->current_url());
+  EXPECT_EQ(url::kHttpScheme, target->current_url().scheme());
   EXPECT_EQ(target->current_origin(), about_blank_origin);
-  EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
-  EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
   EXPECT_FALSE(target->current_origin().opaque());
   EXPECT_EQ("b.com", target->current_origin().host());
   EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 13c995a..8f453562 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -1602,6 +1602,9 @@
   EXPECT_EQ(blank_url, new_root->current_url());
 
   // Make a new iframe in it using document.write from the opener.
+  // Call document.open() outside LoadCommittedCapturer as it implicitly does a
+  // same-document navigation.
+  EXPECT_TRUE(ExecJs(root->current_frame_host(), "w.document.open()"));
   {
     LoadCommittedCapturer capturer(new_shell->web_contents());
     std::string html = "<iframe src='" + url1.spec() + "'></iframe>";
@@ -1613,7 +1616,9 @@
     capturer.Wait();
   }
   ASSERT_EQ(1U, new_root->child_count());
-  EXPECT_EQ(blank_url, new_root->current_url());
+  // Since we did a document.open(), the new root's URL is the same as the
+  // outer URL.
+  EXPECT_EQ(url1, new_root->current_url());
   EXPECT_EQ(url1, new_root->child_at(0)->current_url());
 
   // Navigate the subframe.
@@ -1625,7 +1630,7 @@
     EXPECT_TRUE(ExecJs(new_root->child_at(0), script));
     capturer.Wait();
   }
-  EXPECT_EQ(blank_url, new_root->current_url());
+  EXPECT_EQ(url1, new_root->current_url());
   EXPECT_EQ(url2, new_root->child_at(0)->current_url());
   EXPECT_EQ(2, new_shell->web_contents()->GetController().GetEntryCount());
 
@@ -1660,6 +1665,96 @@
   EXPECT_TRUE(new_root->current_frame_host()->IsRenderFrameLive());
 }
 
+// Test that a frame's url is correctly updated after a document.open() from
+// an about:blank frame.
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       DocumentOpenFromAboutBlank) {
+  GURL url1 = embedded_test_server()->GetURL(
+      "/navigation_controller/page_with_iframe_simple.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  FrameTreeNode* root = contents()->GetFrameTree()->root();
+
+  // Make a new iframe that will document.open() its sibling.
+  {
+    LoadCommittedCapturer capturer(contents());
+    EXPECT_EQ("done", EvalJs(root->current_frame_host(), R"(
+      new Promise(async resolve => {
+        const blank_iframe = document.createElement('iframe');
+        await new Promise(resolve => {
+          blank_iframe.onload = resolve;
+          document.body.appendChild(blank_iframe);
+        });
+
+        let script = document.createElement('script');
+        script.text = `
+          const sibling = parent.document.getElementById("frame")
+          sibling.contentDocument.open();
+        `;
+        blank_iframe.contentDocument.body.appendChild(script);
+        resolve("done");
+      })
+    )"));
+    capturer.Wait();
+  }
+  ASSERT_EQ(2U, root->child_count());
+  EXPECT_EQ(GURL(url::kAboutBlankURL), root->child_at(0)->current_url());
+  EXPECT_EQ(GURL(url::kAboutBlankURL), root->child_at(1)->current_url());
+}
+
+// Test that a frame's url is correctly updated after a document.open() from
+// an about:srcdoc frame.
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       DocumentOpenFromSrcdoc) {
+  GURL url1 = embedded_test_server()->GetURL(
+      "/navigation_controller/page_with_iframe_simple.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  FrameTreeNode* root = contents()->GetFrameTree()->root();
+
+  // Make a new iframe that will document.open() its sibling.
+  {
+    LoadCommittedCapturer capturer(contents());
+    std::string html = "<iframe src='" + url1.spec() + "'></iframe>";
+    std::string script =
+        "let origin = document.createElement('iframe');"
+        "origin.srcdoc = '<script>parent.document.getElementById(\"frame\")"
+        ".contentDocument.open();</s' + 'cript>';"
+        "document.body.appendChild(origin);";
+    EXPECT_TRUE(ExecJs(root->current_frame_host(), script));
+    capturer.Wait();
+  }
+  ASSERT_EQ(2U, root->child_count());
+  EXPECT_EQ("about:srcdoc", root->child_at(0)->current_url());
+  EXPECT_EQ("about:srcdoc", root->child_at(1)->current_url());
+}
+
+// Test that a frame's url is correctly updated after a document.open() from
+// a blob: url
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       DocumentOpenFromBloblIframe) {
+  GURL url1 = embedded_test_server()->GetURL(
+      "/navigation_controller/page_with_iframe_simple.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  FrameTreeNode* root = contents()->GetFrameTree()->root();
+
+  // Make a new iframe that will document.open() its sibling.
+  {
+    LoadCommittedCapturer capturer(contents());
+    std::string html = "<iframe src='" + url1.spec() + "'></iframe>";
+    std::string script =
+        "let origin = document.createElement('iframe');"
+        "let blob = new Blob(['<script>"
+        "parent.document.getElementById(\"frame\").contentDocument.open();"
+        "</s' + 'cript>'], { type: 'text/html' });"
+        "origin.src = URL.createObjectURL(blob);"
+        "document.body.appendChild(origin);";
+    EXPECT_TRUE(ExecJs(root->current_frame_host(), script));
+    capturer.Wait();
+  }
+  ASSERT_EQ(2U, root->child_count());
+  EXPECT_TRUE(root->child_at(0)->current_url().SchemeIsBlob());
+  EXPECT_TRUE(root->child_at(1)->current_url().SchemeIsBlob());
+}
+
 IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest, ErrorPageReplacement) {
   NavigationController& controller = shell()->web_contents()->GetController();
   GURL error_url = embedded_test_server()->GetURL("/close-socket");
@@ -4153,7 +4248,7 @@
   ASSERT_EQ(1U, root->child_at(0)->child_count());
   ASSERT_EQ(0U, root->child_at(0)->child_at(0)->child_count());
   EXPECT_EQ(main_url, root->current_url());
-  EXPECT_EQ(blank_url, root->child_at(0)->current_url());
+  EXPECT_EQ(main_url, root->child_at(0)->current_url());
   EXPECT_EQ(inner_url, root->child_at(0)->child_at(0)->current_url());
 
   EXPECT_EQ(1, controller.GetEntryCount());
@@ -4162,7 +4257,7 @@
 
   // The entry should have FrameNavigationEntries for the subframes.
   ASSERT_EQ(1U, entry->root_node()->children.size());
-  EXPECT_EQ(blank_url, entry->root_node()->children[0]->frame_entry->url());
+  EXPECT_EQ(main_url, entry->root_node()->children[0]->frame_entry->url());
   EXPECT_EQ(inner_url,
             entry->root_node()->children[0]->children[0]->frame_entry->url());
 
@@ -4188,7 +4283,7 @@
   }
   ASSERT_EQ(1U, root->child_count());
   EXPECT_EQ(main_url, root->current_url());
-  EXPECT_EQ(blank_url, root->child_at(0)->current_url());
+  EXPECT_EQ(main_url, root->child_at(0)->current_url());
 
   // Verify that the inner iframe went to the correct URL.
   EXPECT_EQ(inner_url, root->child_at(0)->child_at(0)->current_url());
@@ -4204,7 +4299,7 @@
   ASSERT_EQ(1U, entry->root_node()->children.size());
 
   // The entry should have FrameNavigationEntries for the subframes.
-  EXPECT_EQ(blank_url, entry->root_node()->children[0]->frame_entry->url());
+  EXPECT_EQ(main_url, entry->root_node()->children[0]->frame_entry->url());
   EXPECT_EQ(inner_url,
             entry->root_node()->children[0]->children[0]->frame_entry->url());
 
@@ -6039,9 +6134,13 @@
     // The original request URL will be the first entry of redirect chain,
     // which is the URL that initiated the client redirect. However due to the
     // bug above this will actually result in a blank URL.
-    // TODO(https://crbug.com/1171210): Fix this. Also, figure out why this also
-    // happens when we didn't enter the "about:blank#blocked" part above?
-    EXPECT_EQ(entry->GetOriginalRequestURL(), GURL());
+    // TODO(https://crbug.com/1171210): Fix this.
+    if (AreAllSitesIsolatedForTesting() ||
+        CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
+        EXPECT_EQ(entry->GetOriginalRequestURL(), GURL());
+    } else {
+        EXPECT_EQ(entry->GetRedirectChain()[0], start_url);
+    }
   }
 
   {
@@ -7683,13 +7782,15 @@
   ASSERT_NE(nullptr, frame);
   EXPECT_EQ(blank_url, frame->current_url());
 
-  // Do a document.write in the subframe to create a link to click.
+  // Do a document.write() in the subframe to create a link to click. This sets
+  // the URL to be the same as the frame that called document.write().
   std::string document_write_script =
       "var iframe = document.getElementById('frame');"
       "iframe.contentWindow.document.write("
       "    \"<a id='fraglink' href='#frag'>fragment link</a>\");"
       "iframe.contentWindow.document.close();";
   EXPECT_TRUE(ExecJs(root->current_frame_host(), document_write_script));
+  EXPECT_EQ(links_url, frame->current_url());
 
   // Click the link to do a same document navigation.  Due to the
   // document.write, the new URL matches the parent frame's URL.
@@ -7716,7 +7817,10 @@
   EXPECT_TRUE(ExecJs(root->current_frame_host(), "true;"));
   EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
 
-  EXPECT_EQ(blank_url, frame->current_url());
+  // When we go back in history, the history entry URL should be used. However,
+  // since we did a .write which set the history entry's URL, we go back to the
+  // main page URL.
+  EXPECT_EQ(links_url, frame->current_url());
 }
 
 // Test for same document navigation kills when going back to about:blank in an
@@ -7767,12 +7871,14 @@
       "iframe.contentWindow.document.close();",
       html);
   EXPECT_TRUE(ExecJs(root, document_write_script));
+  EXPECT_EQ(data_url, frame->current_url());
   EXPECT_EQ(opaque_origin, root->current_origin());
   EXPECT_EQ(opaque_origin, frame->current_origin());
 
-  // Click the link to do a same document navigation.  Due to the
+  // Click the link to do a same document navigation. Due to the
   // document.write, the new URL matches the parent frame's URL, but the
-  // opaque origin is preserved.
+  // opaque origin is preserved. Not only that, the history entry's URL is
+  // changed to match the parent frame's URL.
   GURL frame_url_2("data:text/html,Top level page#frag");
   std::string link_script = "document.getElementById('fraglink').click()";
   EXPECT_TRUE(ExecJs(frame, link_script));
@@ -7802,7 +7908,10 @@
   EXPECT_EQ("ping", EvalJs(root, "'ping'"));
   EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
 
-  EXPECT_EQ(blank_url, frame->current_url());
+  // When we go back in history, the history entry URL should be used. However,
+  // since we did a .write which set the history entry's URL, we go back to the
+  // main page URL.
+  EXPECT_EQ(data_url, frame->current_url());
   EXPECT_EQ(opaque_origin, frame->current_origin());
 }
 
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index f7fd3404..7bd4202 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -4671,6 +4671,18 @@
 void NavigationRequest::ReadyToCommitNavigation(bool is_error) {
   EnterChildTraceEvent("ReadyToCommitNavigation", this);
 
+  // We may come back to here asynchronously, and the renderer may be destroyed
+  // in the meantime. Renderer-initiated navigations listen to mojo
+  // disconnection from the renderer NavigationClient; but browser-initiated
+  // navigations do not, so we must look explicitly. We should not proceed and
+  // claim "ReadyToCommitNavigation" to the delegate if the renderer is gone.
+  if (!render_frame_host_->IsRenderFrameLive()) {
+    OnRendererAbortedNavigation();
+    // DO NOT ADD CODE AFTER THIS, as the NavigationHandle has been deleted
+    // by the previous call.
+    return;
+  }
+
   SetState(READY_TO_COMMIT);
   ready_to_commit_time_ = base::TimeTicks::Now();
   RestartCommitTimeout();
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index b820fbf20..6a91a4f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1057,7 +1057,7 @@
   // Only main frames have `waiting_for_init_` set.
   DCHECK(!waiting_for_init_ || !parent_);
 
-  agent_scheduling_group().AddRoute(routing_id_, this);
+  GetAgentSchedulingGroup().AddRoute(routing_id_, this);
   g_routing_id_frame_map.Get().emplace(
       GlobalFrameRoutingId(GetProcess()->GetID(), routing_id_), this);
   g_token_frame_map.Get().insert(std::make_pair(frame_token_, this));
@@ -1274,7 +1274,7 @@
   if (was_created && render_view_host_->GetMainFrame() != this)
     CHECK(IsPendingDeletion() || IsInBackForwardCache());
 
-  agent_scheduling_group().RemoveRoute(routing_id_);
+  GetAgentSchedulingGroup().RemoveRoute(routing_id_);
   g_routing_id_frame_map.Get().erase(
       GlobalFrameRoutingId(GetProcess()->GetID(), routing_id_));
 
@@ -1516,7 +1516,7 @@
   return agent_scheduling_group_.GetProcess();
 }
 
-AgentSchedulingGroupHost& RenderFrameHostImpl::agent_scheduling_group() {
+AgentSchedulingGroupHost& RenderFrameHostImpl::GetAgentSchedulingGroup() {
   return agent_scheduling_group_;
 }
 
@@ -1860,8 +1860,8 @@
   if (!remote_associated_interfaces_) {
     mojo::AssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
         remote_interfaces;
-    if (agent_scheduling_group().GetChannel()) {
-      agent_scheduling_group().GetRemoteRouteProvider()->GetRoute(
+    if (GetAgentSchedulingGroup().GetChannel()) {
+      GetAgentSchedulingGroup().GetRemoteRouteProvider()->GetRoute(
           GetRoutingID(), remote_interfaces.BindNewEndpointAndPassReceiver());
     } else {
       // The channel may not be initialized in some tests environments. In this
@@ -1903,7 +1903,7 @@
 }
 
 bool RenderFrameHostImpl::Send(IPC::Message* message) {
-  return agent_scheduling_group().Send(message);
+  return GetAgentSchedulingGroup().Send(message);
 }
 
 bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message& msg) {
@@ -2277,7 +2277,7 @@
   // initialized it) or may not (we have our own process or the old process
   // crashed) have been initialized. Calling Init() multiple times will be
   // ignored, so this is safe.
-  if (!agent_scheduling_group().Init())
+  if (!GetAgentSchedulingGroup().Init())
     return false;
 
   DCHECK(GetProcess()->IsInitializedAndNotDead());
@@ -2376,7 +2376,7 @@
   // be able to insert the new frame in the frame tree.
   DCHECK(params->previous_routing_id != MSG_ROUTING_NONE ||
          params->parent_routing_id != MSG_ROUTING_NONE);
-  agent_scheduling_group().CreateFrame(std::move(params));
+  GetAgentSchedulingGroup().CreateFrame(std::move(params));
 
   if (previous_routing_id != MSG_ROUTING_NONE) {
     RenderFrameProxyHost* proxy = RenderFrameProxyHost::FromID(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 5e42a6fb..f76b88c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -561,7 +561,6 @@
                    NavigationRequest* navigation_request,
                    bool did_create_new_document);
 
-  virtual AgentSchedulingGroupHost& agent_scheduling_group();
   RenderViewHostImpl* render_view_host() { return render_view_host_.get(); }
   RenderFrameHostDelegate* delegate() { return delegate_; }
   FrameTree* frame_tree() const { return frame_tree_; }
@@ -1013,6 +1012,10 @@
   const mojo::AssociatedRemote<blink::mojom::LocalFrame>&
   GetAssociatedLocalFrame();
 
+  // Returns the AgentSchedulingGroupHost associated with this
+  // RenderFrameHostImpl.
+  virtual AgentSchedulingGroupHost& GetAgentSchedulingGroup();
+
   // Returns associated remote for the blink::mojom::LocalMainFrame Mojo
   // interface. May be overridden by subclasses, e.g. tests which wish to
   // intercept outgoing local main frame messages.
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index b8ace6d..8c15970f 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -2988,6 +2988,14 @@
   TRACE_EVENT1("navigation", "RenderFrameHostManager::CommitPending",
                "FrameTreeNode id", frame_tree_node_->frame_tree_node_id());
   CHECK(pending_rfh);
+  // We either come here with a `pending_rfh` that is
+  // 1) a speculative RenderFrameHost, which would have been deleted
+  //    immediately upon renderer process exit, so it must still have a live
+  //    connection to its renderer frame.
+  // 2) a current RenderFrameHost which has just received a commit IPC from the
+  //    renderer, so it must have a live connection to its renderer frame in
+  //    order to receive the IPC.
+  DCHECK(pending_rfh->IsRenderFrameCreated());
 
   // We should never have a pending bfcache entry if bfcache is disabled.
   DCHECK(!pending_bfcache_entry || IsBackForwardCacheEnabled());
@@ -3009,6 +3017,7 @@
   // Remember if the page was focused so we can focus the new renderer in
   // that case.
   bool focus_render_view =
+      old_view && old_view->HasFocus() &&
       render_frame_host_->GetMainFrame()->GetRenderWidgetHost()->is_focused();
 
   // Remove the current frame and its descendants from the set of fullscreen
@@ -3103,8 +3112,13 @@
   }
 
   RenderWidgetHostView* new_view = render_frame_host_->GetView();
+  // Since the committing renderer frame is live, the RenderWidgetHostView must
+  // also exist. For a local root frame, they share lifetimes exactly. For
+  // another child frame, the RenderWidgetHostView comes from a parent, but if
+  // this renderer frame is live its ancestors must be as well.
+  DCHECK(new_view);
 
-  if (focus_render_view && new_view) {
+  if (focus_render_view) {
     if (is_main_frame) {
       new_view->Focus();
     } else {
@@ -3134,7 +3148,7 @@
 
   // Make the new view show the contents of old view until it has something
   // useful to show.
-  if (is_main_frame && old_view && new_view && old_view != new_view)
+  if (is_main_frame && old_view && old_view != new_view)
     new_view->TakeFallbackContentFrom(old_view);
 
   // The RenderViewHost keeps track of the main RenderFrameHost routing id.
@@ -3213,7 +3227,7 @@
   if (proxy_to_parent)
     proxy_to_parent->SetChildRWHView(new_view, old_size ? &*old_size : nullptr);
 
-  if (render_frame_host_->is_local_root() && new_view) {
+  if (render_frame_host_->is_local_root()) {
     // RenderFrames are created with a hidden RenderWidgetHost. When navigation
     // finishes, we show it if the delegate is shown.
     if (!frame_tree_node_->frame_tree()->IsHidden())
@@ -3223,17 +3237,6 @@
   // The process will no longer try to exit, so we can decrement the count.
   render_frame_host_->GetProcess()->RemovePendingView();
 
-  // If there's no RenderWidgetHostView on this frame's local root (or itself
-  // if it is a local root), then this RenderViewHost died while it was hidden.
-  // We ignored the RenderProcessGone call at the time, so we should send it now
-  // to make sure the sad tab shows up, etc.
-  if (!new_view) {
-    DCHECK(!render_frame_host_->IsRenderFrameLive());
-    DCHECK(!new_rvh->IsRenderViewLive());
-    render_frame_host_->ResetLoadingState();
-    delegate_->RenderProcessGoneFromRenderManager(new_rvh);
-  }
-
   // After all is done, there must never be a proxy in the list which has the
   // same SiteInstance as the current RenderFrameHost.
   CHECK(!GetRenderFrameProxyHost(render_frame_host_->GetSiteInstance()));
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index 15a9549..22d9aeea 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -133,8 +133,6 @@
         bool proceed,
         const base::TimeTicks& proceed_time,
         bool* proceed_to_fire_unload) = 0;
-    virtual void RenderProcessGoneFromRenderManager(
-        RenderViewHost* render_view_host) = 0;
     virtual void CancelModalDialogsForRenderManager() = 0;
     virtual void NotifySwappedFromRenderManager(RenderFrameHost* old_frame,
                                                 RenderFrameHost* new_frame,
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
index 2310739..ee37618 100644
--- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -4049,10 +4049,12 @@
                 kExpectedSiteURL.spec(),
             DepictFrameTree(*root));
 
-  EXPECT_EQ(GURL(url::kAboutBlankURL),
-            root->child_at(0)->child_at(0)->current_url());
+  EXPECT_EQ(url, root->child_at(0)->child_at(0)->current_url());
 
-  EXPECT_FALSE(root->child_at(0)->child_at(0)->has_committed_real_load());
+  // This is true because of the document.open() call, which makes the frame to
+  // be considered to have had committed a real load. The FrameTreeVisualizer
+  // test should be enough to ensure that the childmost frame is not loaded.
+  EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
 }
 
 // Ensure that navigating a subframe to the same URL as its parent twice in a
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 68aed23..02eace6 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -2514,6 +2514,8 @@
   DidNavigateFrame(child, hostB);
 
   // Ensure that the main page is focused.
+  main_test_rfh()->GetView()->Focus();
+  EXPECT_TRUE(main_test_rfh()->GetView()->HasFocus());
   main_test_rfh()->GetRenderWidgetHost()->SetPageFocus(true);
   EXPECT_TRUE(main_test_rfh()->GetRenderWidgetHost()->is_focused());
 
diff --git a/content/browser/resources/histograms/BUILD.gn b/content/browser/resources/histograms/BUILD.gn
index cc4ce92..b5fab5c 100644
--- a/content/browser/resources/histograms/BUILD.gn
+++ b/content/browser/resources/histograms/BUILD.gn
@@ -5,12 +5,13 @@
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
+  uses_js_modules = true
   deps = [ ":histograms_internals" ]
 }
 
 js_library("histograms_internals") {
   deps = [
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:util.m",
   ]
 }
diff --git a/content/browser/resources/histograms/histograms_internals.html b/content/browser/resources/histograms/histograms_internals.html
index 480bf33..e725fb7 100644
--- a/content/browser/resources/histograms/histograms_internals.html
+++ b/content/browser/resources/histograms/histograms_internals.html
@@ -5,11 +5,7 @@
 <html dir="ltr" lang="en">
 <head>
   <meta charset="utf-8">
-  <script src="chrome://resources/js/cr.js"></script>
-  <script src="chrome://resources/js/assert.js"></script>
-  <script src="chrome://resources/js/promise_resolver.js"></script>
-  <script src="chrome://resources/js/util.js"></script>
-  <script src="histograms_internals.js"></script>
+  <script type="module" src="histograms_internals.js"></script>
   <title>Histograms</title>
 </head>
 <h1>Histograms</h1>
diff --git a/content/browser/resources/histograms/histograms_internals.js b/content/browser/resources/histograms/histograms_internals.js
index 00d71ea..5d4786a 100644
--- a/content/browser/resources/histograms/histograms_internals.js
+++ b/content/browser/resources/histograms/histograms_internals.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {$} from 'chrome://resources/js/util.m.js';
+
 /**
  * Initiates the request for histograms.
  */
@@ -10,7 +13,7 @@
   if (document.location.pathname) {
     query = document.location.pathname.substring(1);
   }
-  cr.sendWithPromise('requestHistograms', query).then(addHistograms);
+  sendWithPromise('requestHistograms', query).then(addHistograms);
 }
 
 /**
@@ -29,6 +32,7 @@
     clone.querySelector('p').textContent = body;
     $('histograms').appendChild(clone);
   }
+  $('histograms').dispatchEvent(new CustomEvent('histograms-updated-for-test'));
 }
 
 /**
diff --git a/content/browser/resources/indexed_db/indexeddb_internals.html b/content/browser/resources/indexed_db/indexeddb_internals.html
index c8aef91..aad8e87 100644
--- a/content/browser/resources/indexed_db/indexeddb_internals.html
+++ b/content/browser/resources/indexed_db/indexeddb_internals.html
@@ -166,10 +166,6 @@
     <div class="content">
         <div id="indexeddb-list">
     </div>
-    <script src="chrome://resources/js/assert.js"></script>
-    <script src="chrome://resources/js/util.js"></script>
-    <script src="chrome://resources/js/cr.js"></script>
-    <script src="indexeddb_internals.js"></script>
-    <script src="chrome://resources/js/jstemplate_compiled.js"></script>
+    <script type="module" src="indexeddb_internals.js"></script>
 </body>
 </html>
diff --git a/content/browser/resources/indexed_db/indexeddb_internals.js b/content/browser/resources/indexed_db/indexeddb_internals.js
index 8e63c36..843963d 100644
--- a/content/browser/resources/indexed_db/indexeddb_internals.js
+++ b/content/browser/resources/indexed_db/indexeddb_internals.js
@@ -2,12 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('indexeddb', function() {
-  'use strict';
+import 'chrome://resources/js/jstemplate_compiled.js';
 
-  function initialize() {
-    chrome.send('getAllOrigins');
-  }
+import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {$} from 'chrome://resources/js/util.m.js';
+
+function initialize() {
+  addWebUIListener('origins-ready', onOriginsReady);
+
+  chrome.send('getAllOrigins');
+}
 
   function progressNodeFor(link) {
     return link.parentNode.querySelector('.download-status');
@@ -16,15 +20,22 @@
   function downloadOriginData(event) {
     const link = event.target;
     progressNodeFor(link).style.display = 'inline';
-    chrome.send(
-        'downloadOriginData', [link.idb_partition_path, link.idb_origin_url]);
+    const path = link.idb_partition_path;
+    const origin = link.idb_origin_url;
+    sendWithPromise('downloadOriginData', path, origin)
+        .then(count => onOriginDownloadReady(path, origin, count), () => {
+          console.error('Error downloading data for origin ' + origin);
+        });
     return false;
   }
 
   function forceClose(event) {
     const link = event.target;
     progressNodeFor(link).style.display = 'inline';
-    chrome.send('forceClose', [link.idb_partition_path, link.idb_origin_url]);
+    const path = link.idb_partition_path;
+    const origin = link.idb_origin_url;
+    sendWithPromise('forceClose', path, origin)
+        .then(count => onForcedClose(path, origin, count));
     return false;
   }
 
@@ -78,12 +89,4 @@
     }
   }
 
-  return {
-    initialize: initialize,
-    onForcedClose: onForcedClose,
-    onOriginDownloadReady: onOriginDownloadReady,
-    onOriginsReady: onOriginsReady,
-  };
-});
-
-document.addEventListener('DOMContentLoaded', indexeddb.initialize);
+  document.addEventListener('DOMContentLoaded', initialize);
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm
index f7c7fe3e5..50aa05f 100644
--- a/content/browser/sandbox_parameters_mac.mm
+++ b/content/browser/sandbox_parameters_mac.mm
@@ -39,8 +39,12 @@
 
 namespace {
 
-// Set by SetNetworkTestCertsDirectoryForTesting().
-base::NoDestructor<base::Optional<base::FilePath>> g_network_test_certs_dir;
+base::Optional<base::FilePath>& GetNetworkTestCertsDirectory() {
+  // Set by SetNetworkTestCertsDirectoryForTesting().
+  static base::NoDestructor<base::Optional<base::FilePath>>
+      network_test_certs_dir;
+  return *network_test_certs_dir;
+}
 
 // Produce the OS version as an integer "1010", etc. and pass that to the
 // profile. The profile converts the string back to a number and can do
@@ -160,10 +164,10 @@
     CHECK(client->SetParameter(param_name, path.value())) << param_name;
   }
 
-  if (g_network_test_certs_dir->has_value()) {
+  if (GetNetworkTestCertsDirectory().has_value()) {
     CHECK(client->SetParameter("NETWORK_SERVICE_TEST_CERTS_DIR",
                                sandbox::policy::SandboxMac::GetCanonicalPath(
-                                   **g_network_test_certs_dir)
+                                   *GetNetworkTestCertsDirectory())
                                    .value()));
   }
 }
@@ -212,6 +216,25 @@
   SetupCommonSandboxParameters(client);
 }
 
+void SetupGpuSandboxParameters(sandbox::SeatbeltExecClient* client,
+                               const base::CommandLine& command_line) {
+  SetupCommonSandboxParameters(client);
+  AddDarwinDirs(client);
+  CHECK(client->SetBooleanParameter(
+      sandbox::policy::SandboxMac::kSandboxDisableMetalShaderCache,
+      command_line.HasSwitch(
+          sandbox::policy::switches::kDisableMetalShaderCache)));
+
+  // Temporary for https://crbug.com/1126350.
+  CHECK(client->SetParameter("PARENT_DIR",
+                             sandbox::policy::SandboxMac::GetCanonicalPath(
+                                 base::mac::OuterBundlePath().DirName())
+                                 .value()));
+  base::FilePath pwd;
+  CHECK(base::GetCurrentDirectory(&pwd));
+  CHECK(client->SetParameter("PWD", pwd.value()));
+}
+
 }  // namespace
 
 void SetupSandboxParameters(sandbox::policy::SandboxType sandbox_type,
@@ -225,16 +248,7 @@
       SetupCommonSandboxParameters(client);
       break;
     case sandbox::policy::SandboxType::kGpu: {
-      SetupCommonSandboxParameters(client);
-      // Temporary for https://crbug.com/1126350.
-      CHECK(client->SetParameter("PARENT_DIR",
-                                 sandbox::policy::SandboxMac::GetCanonicalPath(
-                                     base::mac::OuterBundlePath().DirName())
-                                     .value()));
-      base::FilePath pwd;
-      CHECK(base::GetCurrentDirectory(&pwd));
-      CHECK(client->SetParameter("PWD", pwd.value()));
-      AddDarwinDirs(client);
+      SetupGpuSandboxParameters(client, command_line);
       break;
     }
     case sandbox::policy::SandboxType::kCdm:
@@ -265,7 +279,7 @@
 }
 
 void SetNetworkTestCertsDirectoryForTesting(const base::FilePath& path) {
-  g_network_test_certs_dir->emplace(path);
+  GetNetworkTestCertsDirectory().emplace(path);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/fake_service_worker.cc b/content/browser/service_worker/fake_service_worker.cc
index 989df20..16380df 100644
--- a/content/browser/service_worker/fake_service_worker.cc
+++ b/content/browser/service_worker/fake_service_worker.cc
@@ -53,23 +53,18 @@
 
   // Enable callers to use these endpoints without us actually binding them
   // to an implementation.
-  mojo::AssociateWithDisconnectedPipe(registration_info->receiver.PassHandle());
+  registration_info->receiver.EnableUnassociatedUsage();
   if (registration_info->installing) {
-    mojo::AssociateWithDisconnectedPipe(
-        registration_info->installing->receiver.PassHandle());
+    registration_info->installing->receiver.EnableUnassociatedUsage();
   }
   if (registration_info->waiting) {
-    mojo::AssociateWithDisconnectedPipe(
-        registration_info->waiting->receiver.PassHandle());
+    registration_info->waiting->receiver.EnableUnassociatedUsage();
   }
   if (registration_info->active) {
-    mojo::AssociateWithDisconnectedPipe(
-        registration_info->active->receiver.PassHandle());
+    registration_info->active->receiver.EnableUnassociatedUsage();
   }
-
   if (service_worker_info) {
-    mojo::AssociateWithDisconnectedPipe(
-        service_worker_info->receiver.PassHandle());
+    service_worker_info->receiver.EnableUnassociatedUsage();
   }
 
   registration_info_ = std::move(registration_info);
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index d4d6b76..f650216 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -318,7 +318,7 @@
   // no need to pass |object_info_->receiver| through a message pipe endpoint.
   blink::mojom::ServiceWorkerRegistrationObjectInfoPtr object_info =
       registration_object_host->CreateObjectInfo();
-  mojo::AssociateWithDisconnectedPipe(object_info->receiver.PassHandle());
+  object_info->receiver.EnableUnassociatedUsage();
 
   registration->NotifyRegistrationFailed();
   // Don't crash when |registration_object_host| gets destructed.
diff --git a/content/browser/speech/tts_win.cc b/content/browser/speech/tts_win.cc
index e41cab82..c5e2988 100644
--- a/content/browser/speech/tts_win.cc
+++ b/content/browser/speech/tts_win.cc
@@ -530,8 +530,8 @@
 bool TtsPlatformImplWin::StopSpeaking() {
   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::StopSpeaking,
-               paused_);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::StopSpeaking)
+      .WithArgs(paused_);
   paused_ = false;
 
   is_speaking_ = false;
@@ -546,7 +546,7 @@
 
   if (paused_ || !is_speaking_)
     return;
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Pause);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::Pause);
   paused_ = true;
 }
 
@@ -557,7 +557,7 @@
   if (!paused_)
     return;
 
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Resume);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::Resume);
   paused_ = false;
 }
 
@@ -576,7 +576,7 @@
 void TtsPlatformImplWin::Shutdown() {
   // This is required to ensures the object is released before the COM is
   // uninitialized. Otherwise, this is causing shutdown hangs.
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Shutdown);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::Shutdown);
 }
 
 void TtsPlatformImplWin::OnInitializeComplete(bool success,
@@ -622,9 +622,9 @@
     const std::string& parsed_utterance) {
   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::ProcessSpeech,
-               utterance_id, lang, voice, params, std::move(on_speak_finished),
-               parsed_utterance);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::ProcessSpeech)
+      .WithArgs(utterance_id, lang, voice, params, std::move(on_speak_finished),
+                parsed_utterance);
 }
 
 TtsPlatformImplWin::TtsPlatformImplWin()
@@ -632,7 +632,7 @@
           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
       worker_(worker_task_runner_, worker_task_runner_) {
   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Initialize);
+  worker_.AsyncCall(&TtsPlatformImplBackgroundWorker::Initialize);
 }
 
 // static
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index fdb2220..2e461e4a 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -830,8 +830,8 @@
       node_(this),
       frame_tree_(browser_context, this, this, this, this, this, this, this),
       is_load_to_different_document_(false),
-      crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING),
-      crashed_error_code_(0),
+      main_frame_process_status_(base::TERMINATION_STATUS_STILL_RUNNING),
+      main_frame_process_error_code_(0),
       waiting_for_response_(false),
       load_state_(net::LOAD_STATE_IDLE, base::string16()),
       upload_size_(0),
@@ -1919,7 +1919,7 @@
 }
 
 bool WebContentsImpl::IsCrashed() {
-  switch (crashed_status_) {
+  switch (main_frame_process_status_) {
     case base::TERMINATION_STATUS_PROCESS_CRASHED:
     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
@@ -1947,25 +1947,25 @@
   return false;
 }
 
-void WebContentsImpl::SetIsCrashed(base::TerminationStatus status,
-                                   int error_code) {
-  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetIsCrashed", "status",
-                        static_cast<int>(status), "old_status",
-                        static_cast<int>(crashed_status_));
-  if (status == crashed_status_)
+void WebContentsImpl::SetMainFrameProcessStatus(base::TerminationStatus status,
+                                                int error_code) {
+  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetMainFrameProcessStatus",
+                        "status", static_cast<int>(status), "old_status",
+                        static_cast<int>(main_frame_process_status_));
+  if (status == main_frame_process_status_)
     return;
 
-  crashed_status_ = status;
-  crashed_error_code_ = error_code;
+  main_frame_process_status_ = status;
+  main_frame_process_error_code_ = error_code;
   NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
 }
 
 base::TerminationStatus WebContentsImpl::GetCrashedStatus() {
-  return crashed_status_;
+  return main_frame_process_status_;
 }
 
 int WebContentsImpl::GetCrashedErrorCode() {
-  return crashed_error_code_;
+  return main_frame_process_error_code_;
 }
 
 bool WebContentsImpl::IsBeingDestroyed() {
@@ -6298,6 +6298,23 @@
 void WebContentsImpl::RenderFrameCreated(RenderFrameHost* render_frame_host) {
   TRACE_EVENT1("content", "WebContentsImpl::RenderFrameCreated",
                "render_frame_host", static_cast<void*>(render_frame_host));
+  // The WebContents tracks the process state for the main frame's renderer.
+  // TODO(crbug.com/1164280): Under MPArch, with multiple frame trees in a
+  // WebContents, this is intended to just track the main frame of the root
+  // page.
+  if (!render_frame_host->GetParent()) {
+    bool was_crashed = IsCrashed();
+    SetMainFrameProcessStatus(base::TERMINATION_STATUS_STILL_RUNNING, 0);
+
+    // Restore the focus to the tab (otherwise the focus will be on the top
+    // window).
+    if (was_crashed && !FocusLocationBarByDefault()) {
+      if (!delegate_ || delegate_->ShouldFocusPageAfterCrash()) {
+        view_->Focus();
+      }
+    }
+  }
+
   observers_.NotifyObservers(&WebContentsObserver::RenderFrameCreated,
                              render_frame_host);
   UpdateAccessibilityModeOnFrame(render_frame_host);
@@ -6798,16 +6815,6 @@
 
   notify_disconnection_ = true;
 
-  bool was_crashed = IsCrashed();
-  SetIsCrashed(base::TERMINATION_STATUS_STILL_RUNNING, 0);
-
-  // Restore the focus to the tab (otherwise the focus will be on the top
-  // window).
-  if (was_crashed && !FocusLocationBarByDefault() &&
-      (!delegate_ || delegate_->ShouldFocusPageAfterCrash())) {
-    view_->Focus();
-  }
-
   observers_.NotifyObservers(&WebContentsObserver::RenderViewReady);
   view_->RenderViewReady();
 }
@@ -6855,15 +6862,14 @@
   // probably will need to more granularly reset the state here.
   ResetLoadProgressState();
   NotifyDisconnected();
-  SetIsCrashed(status, error_code);
+  SetMainFrameProcessStatus(status, error_code);
 
   TRACE_EVENT0("content",
                "Dispatching WebContentsObserver::RenderViewTerminated");
   // Some observers might destroy WebContents in RenderViewTerminated.
   base::WeakPtr<WebContentsImpl> weak_ptr = weak_factory_.GetWeakPtr();
-  auto crashed_status = GetCrashedStatus();
   for (auto& observer : observers_.observer_list()) {
-    observer.RenderProcessGone(crashed_status);
+    observer.RenderProcessGone(status);
     if (!weak_ptr)
       return;
   }
@@ -7531,12 +7537,6 @@
   // Note: |this| might be deleted at this point.
 }
 
-void WebContentsImpl::RenderProcessGoneFromRenderManager(
-    RenderViewHost* render_view_host) {
-  DCHECK(crashed_status_ != base::TERMINATION_STATUS_STILL_RUNNING);
-  RenderViewTerminated(render_view_host, crashed_status_, crashed_error_code_);
-}
-
 void WebContentsImpl::CancelModalDialogsForRenderManager() {
   OPTIONAL_TRACE_EVENT0("content",
                         "WebContentsImpl::CancelModalDialogsForRenderManager");
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 4355a23e9..a861fc3 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -402,7 +402,6 @@
   bool HasFileSystemAccessHandles() override;
   bool HasPictureInPictureVideo() override;
   bool IsCrashed() override;
-  void SetIsCrashed(base::TerminationStatus status, int error_code) override;
   base::TerminationStatus GetCrashedStatus() override;
   int GetCrashedErrorCode() override;
   bool IsBeingDestroyed() override;
@@ -428,7 +427,6 @@
   WebContentsImpl* GetResponsibleWebContents() override;
   void DidChangeVisibleSecurityState() override;
   void SyncRendererPrefs() override;
-
   void Stop() override;
   void SetPageFrozen(bool frozen) override;
   std::unique_ptr<WebContents> Clone() override;
@@ -540,7 +538,6 @@
   gfx::Size GetSize() override;
   void UpdateWindowControlsOverlay(const gfx::Rect& bounding_rect,
                                    const gfx::Insets& insets) override;
-
 #if defined(OS_ANDROID)
   base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents() override;
   WebContentsAndroid* GetWebContentsAndroid();
@@ -549,7 +546,6 @@
   void RequestFindMatchRects(int current_version) override;
   service_manager::InterfaceProvider* GetJavaInterfaces() override;
 #endif
-
   bool HasRecentInteractiveInputEvent() override;
   void SetIgnoreInputEvents(bool ignore_input_events) override;
 
@@ -972,8 +968,6 @@
       bool proceed,
       const base::TimeTicks& proceed_time,
       bool* proceed_to_fire_unload) override;
-  void RenderProcessGoneFromRenderManager(
-      RenderViewHost* render_view_host) override;
   void CancelModalDialogsForRenderManager() override;
   void NotifySwappedFromRenderManager(RenderFrameHost* old_frame,
                                       RenderFrameHost* new_frame,
@@ -1451,6 +1445,11 @@
   void AddObserver(WebContentsObserver* observer);
   void RemoveObserver(WebContentsObserver* observer);
 
+  // Indicates whether this tab should be considered crashed. The setter will
+  // also notify the delegate when the flag is changed.
+  void SetMainFrameProcessStatus(base::TerminationStatus status,
+                                 int error_code);
+
   // Clears a pending contents that has been closed before being shown.
   void OnWebContentsDestroyed(WebContentsImpl* web_contents);
 
@@ -1764,9 +1763,15 @@
   // TODO(pbos): Check navigation requests and handles instead of caching this.
   bool is_load_to_different_document_;
 
-  // Indicates if the tab is considered crashed.
-  base::TerminationStatus crashed_status_;
-  int crashed_error_code_;
+  // Indicates the process state of the primary main frame's renderer process.
+  // If the process is not live due to a crash, this will be reflected by
+  // IsCrashed(), though it's possible to not be live while not indicating a
+  // crash occurred.
+  // TODO(crbug.com/1164280): Under MPArch, with multiple frame trees in a
+  // WebContents, this just tracks the renderer process of the main frame of the
+  // root page. It should be named appropriately.
+  base::TerminationStatus main_frame_process_status_;
+  int main_frame_process_error_code_;
 
   // Whether this WebContents is waiting for a first-response for the
   // main resource of the page. This controls whether the throbber state is
diff --git a/content/browser/web_package/link_web_bundle_browsertest.cc b/content/browser/web_package/link_web_bundle_browsertest.cc
index dd4e8d8..f8b8ef0 100644
--- a/content/browser/web_package/link_web_bundle_browsertest.cc
+++ b/content/browser/web_package/link_web_bundle_browsertest.cc
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/optional.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/common/content_client.h"
@@ -76,6 +80,16 @@
   base::Optional<net::Error> error_code_;
 };
 
+int64_t GetTestDataFileSize(const base::FilePath::CharType* file_path) {
+  int64_t file_size;
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FilePath test_data_dir;
+  CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
+  CHECK(base::GetFileSize(test_data_dir.Append(base::FilePath(file_path)),
+                          &file_size));
+  return file_size;
+}
+
 }  // namespace
 
 class LinkWebBundleBrowserTest : public ContentBrowserTest {
@@ -123,6 +137,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(LinkWebBundleBrowserTest, SubframeLoad) {
+  base::HistogramTester histogram_tester;
   GURL url(embedded_test_server()->GetURL("/web_bundle/link_web_bundle.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -137,6 +152,15 @@
       "document.body.appendChild(iframe);");
   run_loop.Run();
   EXPECT_EQ(net::OK, *finish_navigation_observer.error_code());
+
+  // Check the metrics recorded in the network process.
+  FetchHistogramsFromChildProcesses();
+  int64_t web_bundle_size = GetTestDataFileSize(
+      FILE_PATH_LITERAL("content/test/data/web_bundle/urn-uuid.wbn"));
+  histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
+                                      web_bundle_size, 1);
+  histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ContentLength",
+                                      web_bundle_size, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(LinkWebBundleBrowserTest, FollowLink) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
index 43656db..8389943 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
@@ -27,7 +27,6 @@
 import org.chromium.content.browser.ServicificationStartupUma.ServicificationStartup;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.ui.resources.ResourceExtractor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -59,10 +58,6 @@
 
     private static boolean sShouldStartGpuProcessOnBrowserStartup;
 
-    private static void setShouldStartGpuProcessOnBrowserStartup(boolean enable) {
-        sShouldStartGpuProcessOnBrowserStartup = enable;
-    }
-
     @VisibleForTesting
     @CalledByNative
     static void browserStartupComplete(int result) {
@@ -97,8 +92,8 @@
     // Whether the async startup of the browser process has started.
     private boolean mHasStartedInitializingBrowserProcess;
 
-    // Whether tasks that occur after resource extraction have been completed.
-    private boolean mPostResourceExtractionTasksCompleted;
+    // Ensures prepareToStartBrowserProcess() logic happens only once.
+    private boolean mPrepareToStartCompleted;
 
     private boolean mHasCalledContentStart;
 
@@ -208,23 +203,17 @@
             // This is the first time we have been asked to start the browser process. We set the
             // flag that indicates that we have kicked off starting the browser process.
             mHasStartedInitializingBrowserProcess = true;
+            sShouldStartGpuProcessOnBrowserStartup = startGpuProcess;
+            prepareToStartBrowserProcess(false);
 
-            setShouldStartGpuProcessOnBrowserStartup(startGpuProcess);
-
-            prepareToStartBrowserProcess(false, new Runnable() {
-                @Override
-                public void run() {
-                    ThreadUtils.assertOnUiThread();
-                    if (mHasCalledContentStart) return;
-                    mCurrentBrowserStartType = startMinimalBrowser
-                            ? BrowserStartType.MINIMAL_BROWSER
-                            : BrowserStartType.FULL_BROWSER;
-                    if (contentStart() > 0) {
-                        // Failed. The callbacks may not have run, so run them.
-                        enqueueCallbackExecution(STARTUP_FAILURE);
-                    }
+            if (!mHasCalledContentStart) {
+                mCurrentBrowserStartType = startMinimalBrowser ? BrowserStartType.MINIMAL_BROWSER
+                                                               : BrowserStartType.FULL_BROWSER;
+                if (contentStart() > 0) {
+                    // Failed. The callbacks may not have run, so run them.
+                    enqueueCallbackExecution(STARTUP_FAILURE);
                 }
-            });
+            }
         } else if (mMinimalBrowserStarted && mLaunchFullBrowserAfterMinimalBrowserStart) {
             // If we missed the minimalBrowserStarted() call, launch the full browser now if needed.
             // Otherwise, minimalBrowserStarted() will handle the full browser launch.
@@ -243,12 +232,7 @@
 
         // If already started skip to checking the result
         if (!mFullBrowserStartupDone) {
-            if (!mHasStartedInitializingBrowserProcess || !mPostResourceExtractionTasksCompleted) {
-                try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
-                             "BrowserStartupController.prepareToStartBrowserProcess")) {
-                    prepareToStartBrowserProcess(singleProcess, null);
-                }
-            }
+            prepareToStartBrowserProcess(singleProcess);
 
             boolean startedSuccessfully = true;
             if (!mHasCalledContentStart) {
@@ -438,46 +422,31 @@
     }
 
     @VisibleForTesting
-    void prepareToStartBrowserProcess(
-            final boolean singleProcess, final Runnable completionCallback) {
-        Log.d(TAG, "Initializing chromium process, singleProcess=%b", singleProcess);
-
-        // This strictmode exception is to cover the case where the browser process is being started
-        // asynchronously but not in the main browser flow.  The main browser flow will trigger
-        // library loading earlier and this will be a no-op, but in the other cases this will need
-        // to block on loading libraries.
-        // This applies to tests and ManageSpaceActivity, which can be launched from Settings.
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            // Normally Main.java will have already loaded the library asynchronously, we only need
-            // to load it here if we arrived via another flow, e.g. bookmark access & sync setup.
-            LibraryLoader.getInstance().ensureInitialized();
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
+    void prepareToStartBrowserProcess(final boolean singleProcess) {
+        if (mPrepareToStartCompleted) {
+            return;
         }
-
-        Runnable postResourceExtraction = new Runnable() {
-            @Override
-            public void run() {
-                if (!mPostResourceExtractionTasksCompleted) {
-                    // TODO(yfriedman): Remove dependency on a command line flag for this.
-                    DeviceUtilsImpl.addDeviceSpecificUserAgentSwitch();
-                    BrowserStartupControllerImplJni.get().setCommandLineFlags(singleProcess);
-                    mPostResourceExtractionTasksCompleted = true;
-                }
-
-                if (completionCallback != null) completionCallback.run();
+        Log.d(TAG, "Initializing chromium process, singleProcess=%b", singleProcess);
+        mPrepareToStartCompleted = true;
+        try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("prepareToStartBrowserProcess")) {
+            // This strictmode exception is to cover the case where the browser process is being
+            // started asynchronously but not in the main browser flow.  The main browser flow will
+            // trigger library loading earlier and this will be a no-op, but in the other cases this
+            // will need to block on loading libraries. This applies to tests and
+            // ManageSpaceActivity, which can be launched from Settings.
+            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            try {
+                // Normally Main.java will have already loaded the library asynchronously, we only
+                // need to load it here if we arrived via another flow, e.g. bookmark access & sync
+                // setup.
+                LibraryLoader.getInstance().ensureInitialized();
+            } finally {
+                StrictMode.setThreadPolicy(oldPolicy);
             }
-        };
 
-        ResourceExtractor.get().setResultTraits(UiThreadTaskTraits.BOOTSTRAP);
-        if (completionCallback == null) {
-            // If no continuation callback is specified, then force the resource extraction
-            // to complete.
-            ResourceExtractor.get().waitForCompletion();
-            postResourceExtraction.run();
-        } else {
-            ResourceExtractor.get().addCompletionCallback(postResourceExtraction);
+            // TODO(yfriedman): Remove dependency on a command line flag for this.
+            DeviceUtilsImpl.addDeviceSpecificUserAgentSwitch();
+            BrowserStartupControllerImplJni.get().setCommandLineFlags(singleProcess);
         }
     }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
index 070eb291..381a62a 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
@@ -37,11 +37,9 @@
         private boolean mMinimalBrowserStarted;
 
         @Override
-        void prepareToStartBrowserProcess(boolean singleProcess, Runnable completionCallback) {
+        void prepareToStartBrowserProcess(boolean singleProcess) {
             if (!mLibraryLoadSucceeds) {
                 throw new ProcessInitException(LoaderErrors.NATIVE_LIBRARY_LOAD_FAILED);
-            } else if (completionCallback != null) {
-                completionCallback.run();
             }
         }
 
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 05b19394..8a913c6a 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -590,10 +590,10 @@
   // Indicates whether a video is in Picture-in-Picture for |this|.
   virtual bool HasPictureInPictureVideo() = 0;
 
-  // Indicates whether this tab should be considered crashed. The setter will
-  // also notify the delegate when the flag is changed.
+  // Indicates whether this tab should be considered crashed. This becomes false
+  // again when the renderer process is recreated after a crash in order to
+  // recreate the main frame.
   virtual bool IsCrashed() = 0;
-  virtual void SetIsCrashed(base::TerminationStatus status, int error_code) = 0;
 
   virtual base::TerminationStatus GetCrashedStatus() = 0;
   virtual int GetCrashedErrorCode() = 0;
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
index 640d01f..a9630ea 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
@@ -8,8 +8,6 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.content_public.browser.BrowserStartupController;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.ui.resources.ResourceExtractor;
 
 /**
  * Provides test support for loading and dealing with native libraries.
@@ -40,12 +38,6 @@
     private static void nativeInitialization(boolean initBrowserProcess) {
         LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER);
         if (initBrowserProcess) {
-            // Extract compressed resource paks.
-            ResourceExtractor resourceExtractor = ResourceExtractor.get();
-            resourceExtractor.setResultTraits(UiThreadTaskTraits.BOOTSTRAP);
-            resourceExtractor.startExtractingResources("en");
-            resourceExtractor.waitForCompletion();
-
             BrowserStartupController.getInstance().startBrowserProcessesSync(
                     LibraryProcessType.PROCESS_BROWSER, false);
         } else {
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index d4abda4..3ce7b43 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -641,12 +641,17 @@
   return ExecuteScriptWithoutUserGesture(web_contents, script);
 }
 
-void NavigateToURLBlockUntilNavigationsComplete(WebContents* web_contents,
-                                                const GURL& url,
-                                                int number_of_navigations) {
+void NavigateToURLBlockUntilNavigationsComplete(
+    WebContents* web_contents,
+    const GURL& url,
+    int number_of_navigations,
+    bool ignore_uncommitted_navigations) {
   // Prepare for the navigation.
   WaitForLoadStop(web_contents);
-  TestNavigationObserver same_tab_observer(web_contents, number_of_navigations);
+  TestNavigationObserver same_tab_observer(
+      web_contents, number_of_navigations,
+      MessageLoopRunner::QuitMode::IMMEDIATE,
+      /*ignore_uncommitted_navigations=*/ignore_uncommitted_navigations);
 
   // This mimics behavior of Shell::LoadURL...
   NavigationController::LoadURLParams params(url);
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index db86181..fc6bacc 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -136,10 +136,13 @@
                                       const GURL& expected_commit_url);
 
 // Navigates |web_contents| to |url|, blocking until the given number of
-// navigations finishes.
-void NavigateToURLBlockUntilNavigationsComplete(WebContents* web_contents,
-                                                const GURL& url,
-                                                int number_of_navigations);
+// navigations finishes. If |ignore_uncommitted_navigations| is true, then an
+// aborted navigation also counts toward |number_of_navigations| being complete.
+void NavigateToURLBlockUntilNavigationsComplete(
+    WebContents* web_contents,
+    const GURL& url,
+    int number_of_navigations,
+    bool ignore_uncommitted_navigations = true);
 
 // Perform a renderer-initiated navigation of |window| to |url|, blocking
 // until the navigation finishes.  The navigation is done by assigning
diff --git a/content/public/test/content_browser_test_utils.cc b/content/public/test/content_browser_test_utils.cc
index 41158af..557cf206 100644
--- a/content/public/test/content_browser_test_utils.cc
+++ b/content/public/test/content_browser_test_utils.cc
@@ -54,11 +54,14 @@
   return net::FilePathToFileURL(GetTestFilePath(dir, file));
 }
 
-void NavigateToURLBlockUntilNavigationsComplete(Shell* window,
-                                                const GURL& url,
-                                                int number_of_navigations) {
+void NavigateToURLBlockUntilNavigationsComplete(
+    Shell* window,
+    const GURL& url,
+    int number_of_navigations,
+    bool ignore_uncommitted_navigations) {
   NavigateToURLBlockUntilNavigationsComplete(window->web_contents(), url,
-                                             number_of_navigations);
+                                             number_of_navigations,
+                                             ignore_uncommitted_navigations);
 }
 
 void ReloadBlockUntilNavigationsComplete(Shell* window,
@@ -95,7 +98,7 @@
 bool NavigateToURLAndExpectNoCommit(Shell* window, const GURL& url) {
   NavigationEntry* old_entry =
       window->web_contents()->GetController().GetLastCommittedEntry();
-  NavigateToURLBlockUntilNavigationsComplete(window, url, 1);
+  NavigateToURLBlockUntilNavigationsComplete(window->web_contents(), url, 1);
   NavigationEntry* new_entry =
       window->web_contents()->GetController().GetLastCommittedEntry();
   return old_entry == new_entry;
diff --git a/content/public/test/content_browser_test_utils.h b/content/public/test/content_browser_test_utils.h
index 6e6e095..f472121 100644
--- a/content/public/test/content_browser_test_utils.h
+++ b/content/public/test/content_browser_test_utils.h
@@ -87,10 +87,13 @@
                                       const GURL& expected_commit_url);
 
 // Navigates |window| to |url|, blocking until the given number of navigations
-// finishes.
-void NavigateToURLBlockUntilNavigationsComplete(Shell* window,
-                                                const GURL& url,
-                                                int number_of_navigations);
+// finishes. If |ignore_uncommitted_navigations| is true, then an aborted
+// navigation also counts toward |number_of_navigations| being complete.
+void NavigateToURLBlockUntilNavigationsComplete(
+    Shell* window,
+    const GURL& url,
+    int number_of_navigations,
+    bool ignore_uncommitted_navigations = true);
 
 // Navigates |window| to |url|, blocks until the navigation finishes, and
 // checks that the navigation did not commit (e.g., due to a crash or
diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h
index e1c890bf..95885c0 100644
--- a/content/public/test/web_contents_tester.h
+++ b/content/public/test/web_contents_tester.h
@@ -96,6 +96,9 @@
   // main frame of |opener|.
   virtual void SetOpener(WebContents* opener) = 0;
 
+  // Sets the process state for the primary main frame renderer.
+  virtual void SetIsCrashed(base::TerminationStatus status, int error_code) = 0;
+
   // Returns headers that were passed in the previous SaveFrameWithHeaders(...)
   // call.
   virtual const std::string& GetSaveFrameHeaders() = 0;
diff --git a/content/test/data/accessibility/aria/aria-owns-included-in-tree-expected-blink.txt b/content/test/data/accessibility/aria/aria-owns-included-in-tree-expected-blink.txt
index c025fd2..4d18bbe 100644
--- a/content/test/data/accessibility/aria/aria-owns-included-in-tree-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-owns-included-in-tree-expected-blink.txt
@@ -3,10 +3,11 @@
 ++++genericContainer ignored
 ++++++splitter horizontal name='Control: elements that are not in tree'
 ++++++layoutTable
-++++++++layoutTableRow
-++++++++++layoutTableCell
-++++++++++++textField
-++++++++++++++genericContainer
+++++++++genericContainer ignored
+++++++++++layoutTableRow
+++++++++++++layoutTableCell
+++++++++++++++textField
+++++++++++++++++genericContainer
 ++++++splitter horizontal name='An aria-owned element is always in tree'
 ++++++group
 ++++++++genericContainer ignored
diff --git a/content/test/data/accessibility/css/table-display-expected-blink.txt b/content/test/data/accessibility/css/table-display-expected-blink.txt
index 7e735bb..e27a7782 100644
--- a/content/test/data/accessibility/css/table-display-expected-blink.txt
+++ b/content/test/data/accessibility/css/table-display-expected-blink.txt
@@ -2,16 +2,17 @@
 ++genericContainer ignored
 ++++genericContainer ignored
 ++++++layoutTable
-++++++++layoutTableRow
-++++++++++layoutTableCell name='Cats'
-++++++++++++staticText name='Cats'
-++++++++++++++inlineTextBox name='Cats'
-++++++++++layoutTableCell name='Dogs'
-++++++++++++staticText name='Dogs'
-++++++++++++++inlineTextBox name='Dogs'
-++++++++++layoutTableCell name='Iguanas'
-++++++++++++staticText name='Iguanas'
-++++++++++++++inlineTextBox name='Iguanas'
-++++++++++layoutTableCell name='Fish'
-++++++++++++staticText name='Fish'
-++++++++++++++inlineTextBox name='Fish'
+++++++++genericContainer ignored
+++++++++++layoutTableRow
+++++++++++++layoutTableCell name='Cats'
+++++++++++++++staticText name='Cats'
+++++++++++++++++inlineTextBox name='Cats'
+++++++++++++layoutTableCell name='Dogs'
+++++++++++++++staticText name='Dogs'
+++++++++++++++++inlineTextBox name='Dogs'
+++++++++++++layoutTableCell name='Iguanas'
+++++++++++++++staticText name='Iguanas'
+++++++++++++++++inlineTextBox name='Iguanas'
+++++++++++++layoutTableCell name='Fish'
+++++++++++++++staticText name='Fish'
+++++++++++++++++inlineTextBox name='Fish'
diff --git a/content/test/data/accessibility/css/table-display-other-expected-blink.txt b/content/test/data/accessibility/css/table-display-other-expected-blink.txt
index 05a4fcf..fdf06d7 100644
--- a/content/test/data/accessibility/css/table-display-other-expected-blink.txt
+++ b/content/test/data/accessibility/css/table-display-other-expected-blink.txt
@@ -2,7 +2,8 @@
 ++genericContainer ignored
 ++++genericContainer ignored
 ++++++layoutTable
-++++++++layoutTableRow
-++++++++++layoutTableCell name='cat'
-++++++++++++staticText name='cat'
-++++++++++++++inlineTextBox name='cat'
+++++++++genericContainer ignored
+++++++++++layoutTableRow
+++++++++++++layoutTableCell name='cat'
+++++++++++++++staticText name='cat'
+++++++++++++++++inlineTextBox name='cat'
diff --git a/content/test/data/accessibility/html/table-layout-expected-blink.txt b/content/test/data/accessibility/html/table-layout-expected-blink.txt
index 9ccee06d..bd0bece 100644
--- a/content/test/data/accessibility/html/table-layout-expected-blink.txt
+++ b/content/test/data/accessibility/html/table-layout-expected-blink.txt
@@ -2,33 +2,34 @@
 ++genericContainer ignored
 ++++genericContainer ignored
 ++++++layoutTable
-++++++++layoutTableRow
-++++++++++layoutTableCell name='1'
-++++++++++++staticText name='1'
-++++++++++++++inlineTextBox name='1'
-++++++++++layoutTableCell name='2'
-++++++++++++staticText name='2'
-++++++++++++++inlineTextBox name='2'
-++++++++++layoutTableCell name='3'
-++++++++++++staticText name='3'
-++++++++++++++inlineTextBox name='3'
-++++++++layoutTableRow
-++++++++++layoutTableCell name='4'
-++++++++++++staticText name='4'
-++++++++++++++inlineTextBox name='4'
-++++++++++layoutTableCell name='5'
-++++++++++++staticText name='5'
-++++++++++++++inlineTextBox name='5'
-++++++++++layoutTableCell name='6'
-++++++++++++staticText name='6'
-++++++++++++++inlineTextBox name='6'
-++++++++layoutTableRow
-++++++++++layoutTableCell name='7'
-++++++++++++staticText name='7'
-++++++++++++++inlineTextBox name='7'
-++++++++++layoutTableCell name='8'
-++++++++++++staticText name='8'
-++++++++++++++inlineTextBox name='8'
-++++++++++layoutTableCell name='9'
-++++++++++++staticText name='9'
-++++++++++++++inlineTextBox name='9'
+++++++++genericContainer ignored
+++++++++++layoutTableRow
+++++++++++++layoutTableCell name='1'
+++++++++++++++staticText name='1'
+++++++++++++++++inlineTextBox name='1'
+++++++++++++layoutTableCell name='2'
+++++++++++++++staticText name='2'
+++++++++++++++++inlineTextBox name='2'
+++++++++++++layoutTableCell name='3'
+++++++++++++++staticText name='3'
+++++++++++++++++inlineTextBox name='3'
+++++++++++layoutTableRow
+++++++++++++layoutTableCell name='4'
+++++++++++++++staticText name='4'
+++++++++++++++++inlineTextBox name='4'
+++++++++++++layoutTableCell name='5'
+++++++++++++++staticText name='5'
+++++++++++++++++inlineTextBox name='5'
+++++++++++++layoutTableCell name='6'
+++++++++++++++staticText name='6'
+++++++++++++++++inlineTextBox name='6'
+++++++++++layoutTableRow
+++++++++++++layoutTableCell name='7'
+++++++++++++++staticText name='7'
+++++++++++++++++inlineTextBox name='7'
+++++++++++++layoutTableCell name='8'
+++++++++++++++staticText name='8'
+++++++++++++++++inlineTextBox name='8'
+++++++++++++layoutTableCell name='9'
+++++++++++++++staticText name='9'
+++++++++++++++++inlineTextBox name='9'
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 7ca77c6..789636dc7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -940,6 +940,7 @@
 crbug.com/1165751 [ android-lollipop ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
 crbug.com/1165751 [ android-marshmallow ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
 crbug.com/1165751 [ android-nougat ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
+crbug.com/1165751 [ android-pie ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
 
 # Video tests are flaky. Sometimes the video is black.
 crbug.com/948894 [ android ] conformance/textures/video/* [ RetryOnFailure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 240d335..3249c5df 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -174,6 +174,8 @@
 crbug.com/1146483 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-video-transparent.html [ Skip ]
 crbug.com/1146483 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-alpha-alpha-unsigned_byte.html [ Skip ]
 crbug.com/1146483 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-alpha-alpha-unsigned_byte.html [ Skip ]
+# Need to adjust tolerances of this test.
+crbug.com/1165751 [ fuchsia fuchsia-board-astro ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
 
 # Flaky on Fuchsia hardware devices
 [ fuchsia fuchsia-board-astro ] WebglExtension_EXT_float_blend [ Skip ]
@@ -259,6 +261,7 @@
 crbug.com/angleproject/4922 [ win intel angle-vulkan passthrough ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
 # Need to adjust tolerances of this test.
 crbug.com/1165751 [ win intel angle-vulkan passthrough ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
+crbug.com/1165751 [ win nvidia angle-vulkan passthrough ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
 
 ####################
 # Fuchsia failures #
@@ -742,6 +745,8 @@
 # Flaky renderer hangs suspected due to some driver issue on Kevin devices.
 crbug.com/1091562 [ chromeos-board-kevin ] * [ RetryOnFailure ]
 
+crbug.com/1165751 [ chromeos chromeos-board-kevin ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
+
 ########################
 # SwiftShader failures #
 ########################
diff --git a/content/test/task_runner_deferring_throttle.cc b/content/test/task_runner_deferring_throttle.cc
index c57f8e6..19476bb 100644
--- a/content/test/task_runner_deferring_throttle.cc
+++ b/content/test/task_runner_deferring_throttle.cc
@@ -57,14 +57,18 @@
   return "TaskRunnerDeferringThrottle";
 }
 
+void TaskRunnerDeferringThrottle::AsyncResume() {
+  Resume();
+}
+
 NavigationThrottle::ThrottleCheckResult
 TaskRunnerDeferringThrottle::DeferToPostTask() {
   task_runner_->PostTaskAndReply(
       FROM_HERE, base::DoNothing(),
-      base::BindOnce(&TaskRunnerDeferringThrottle::Resume,
+      base::BindOnce(&TaskRunnerDeferringThrottle::AsyncResume,
                      weak_factory_.GetWeakPtr()));
 
   return NavigationThrottle::DEFER;
 }
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/test/task_runner_deferring_throttle.h b/content/test/task_runner_deferring_throttle.h
index a887a5f..68ba355 100644
--- a/content/test/task_runner_deferring_throttle.h
+++ b/content/test/task_runner_deferring_throttle.h
@@ -34,6 +34,10 @@
   ThrottleCheckResult WillProcessResponse() override;
   const char* GetNameForLogging() override;
 
+ protected:
+  // Tests may override this to change how/when Resume() is called.
+  virtual void AsyncResume();
+
  private:
   ThrottleCheckResult DeferToPostTask();
 
@@ -47,4 +51,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_TEST_TASK_RUNNER_DEFERRING_THROTTLE_H_
\ No newline at end of file
+#endif  // CONTENT_TEST_TASK_RUNNER_DEFERRING_THROTTLE_H_
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index d8056fe0..a8eb508b 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -93,9 +93,9 @@
   return static_cast<MockRenderProcessHost*>(RenderFrameHostImpl::GetProcess());
 }
 
-MockAgentSchedulingGroupHost& TestRenderFrameHost::agent_scheduling_group() {
+MockAgentSchedulingGroupHost& TestRenderFrameHost::GetAgentSchedulingGroup() {
   return static_cast<MockAgentSchedulingGroupHost&>(
-      RenderFrameHostImpl::agent_scheduling_group());
+      RenderFrameHostImpl::GetAgentSchedulingGroup());
 }
 
 TestRenderWidgetHost* TestRenderFrameHost::GetRenderWidgetHost() {
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index 2127fa1..6a75c3a 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -66,7 +66,7 @@
   // RenderFrameHostImpl overrides (same values, but in Test*/Mock* types)
   TestRenderViewHost* GetRenderViewHost() override;
   MockRenderProcessHost* GetProcess() override;
-  MockAgentSchedulingGroupHost& agent_scheduling_group() override;
+  MockAgentSchedulingGroupHost& GetAgentSchedulingGroup() override;
   TestRenderWidgetHost* GetRenderWidgetHost() override;
   void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
                            const std::string& message) override;
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 9cd9fba..35ee654 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -302,6 +302,11 @@
       static_cast<WebContentsImpl*>(opener)->GetFrameTree()->root());
 }
 
+void TestWebContents::SetIsCrashed(base::TerminationStatus status,
+                                   int error_code) {
+  SetMainFrameProcessStatus(status, error_code);
+}
+
 void TestWebContents::AddPendingContents(
     std::unique_ptr<WebContentsImpl> contents,
     const GURL& target_url) {
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 20bff22..715d1b4 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -75,6 +75,7 @@
   void NavigateAndFail(const GURL& url, int error_code) override;
   void TestSetIsLoading(bool value) override;
   void SetOpener(WebContents* opener) override;
+  void SetIsCrashed(base::TerminationStatus status, int error_code) override;
   const std::string& GetSaveFrameHeaders() override;
   const base::string16& GetSuggestedFileName() override;
   bool HasPendingDownloadImage(const GURL& url) override;
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc
index 6eda72d..520587a7 100644
--- a/content/test/web_contents_observer_consistency_checker.cc
+++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -213,9 +213,9 @@
   CHECK(NavigationIsOngoing(navigation_handle));
 
   CHECK(!navigation_handle->HasCommitted());
-  CHECK(navigation_handle->GetRenderFrameHost());
   CHECK_EQ(navigation_handle->GetWebContents(), web_contents());
-  CHECK(navigation_handle->GetRenderFrameHost() != nullptr);
+  CHECK(navigation_handle->GetRenderFrameHost());
+  CHECK(navigation_handle->GetRenderFrameHost()->IsRenderFrameLive());
 
   ready_to_commit_hosts_.insert(
       std::make_pair(navigation_handle->GetNavigationId(),
@@ -232,10 +232,11 @@
   CHECK_EQ(navigation_handle->GetWebContents(), web_contents());
 
   CHECK(!navigation_handle->HasCommitted() ||
-        navigation_handle->GetRenderFrameHost() != nullptr);
-
+        navigation_handle->GetRenderFrameHost());
   CHECK(!navigation_handle->HasCommitted() ||
         navigation_handle->GetRenderFrameHost()->IsCurrent());
+  CHECK(!navigation_handle->HasCommitted() ||
+        navigation_handle->GetRenderFrameHost()->IsRenderFrameLive());
 
   // If ReadyToCommitNavigation was dispatched, verify that the
   // |navigation_handle| has the same RenderFrameHost at this time as the one
diff --git a/device/vr/android/arcore/arcore.cc b/device/vr/android/arcore/arcore.cc
index 8fe97945..0128839 100644
--- a/device/vr/android/arcore/arcore.cc
+++ b/device/vr/android/arcore/arcore.cc
@@ -25,6 +25,18 @@
          gfx::Transform(1, 0, 0, -1, 0, 1);
 }
 
+gfx::Transform ArCore::GetDepthUvFromScreenUvTransform() const {
+  //
+  // Observe how kInputCoordinatesForTransform are transformed by ArCore &
+  // compute a matrix based on that. This is different than the camera UV
+  // transform in that it does not perform the Y-flip - the depth buffer's
+  // coordinate system is defined the same way as ArCore's
+  // AR_COORDINATES_2D_TEXTURE_NORMALIZED.
+  //
+  return MatrixFromTransformedPoints(
+      TransformDisplayUvCoords(kInputCoordinatesForTransform));
+}
+
 ArCore::InitializeResult::InitializeResult(
     const std::unordered_set<device::mojom::XRSessionFeature>& enabled_features,
     base::Optional<device::mojom::XRDepthConfig> depth_configuration)
diff --git a/device/vr/android/arcore/arcore.h b/device/vr/android/arcore/arcore.h
index 61b08b8..cfd4d479 100644
--- a/device/vr/android/arcore/arcore.h
+++ b/device/vr/android/arcore/arcore.h
@@ -89,6 +89,7 @@
   virtual void SetCameraTexture(uint32_t camera_texture_id) = 0;
 
   gfx::Transform GetCameraUvFromScreenUvTransform() const;
+  gfx::Transform GetDepthUvFromScreenUvTransform() const;
 
   virtual gfx::Transform GetProjectionMatrix(float near, float far) = 0;
 
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index 5eed8a89..8f2089d 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -1860,7 +1860,7 @@
 
     result->pixel_data = std::move(pixels);
     // Transform needed to consume the data:
-    result->norm_texture_from_norm_view = GetCameraUvFromScreenUvTransform();
+    result->norm_texture_from_norm_view = GetDepthUvFromScreenUvTransform();
     result->size = gfx::Size(width, height);
     result->raw_value_to_meters =
         1.0 / 1000.0;  // DepthInMillimeters * 1/1000 = DepthInMeters
diff --git a/docs/README.md b/docs/README.md
index 237ef16..0836b43 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -305,6 +305,7 @@
     *   [Kiosk mode and public sessions](enterprise/kiosk_public_session.md)
 *   [Debugging UI in OOBE/login/lock](login/ui_debugging.md)
 *   [Chrome Logging on Chrome OS](chrome_os_logging.md)
+*   [Debugging tips](testing/chromeos_debugging_tips.md)
 
 ### Misc WebUI-Specific Docs
 *   [Creating WebUI Interfaces in components/](webui_in_components.md) How to
diff --git a/docs/speed/perf_lab_platforms.md b/docs/speed/perf_lab_platforms.md
index 6d563af..808b0fb 100644
--- a/docs/speed/perf_lab_platforms.md
+++ b/docs/speed/perf_lab_platforms.md
@@ -20,8 +20,8 @@
 
 ## Linux
 
- * [linux-perf](https://ci.chromium.org/p/chrome/builders/ci/linux-perf): Ubuntu-14.04, 8 core, NVIDIA Quadro P400.
- * [linux-perf-rel](https://ci.chromium.org/p/chrome/builders/ci/linux-perf-rel): Ubuntu-14.04, 8 core, NVIDIA Quadro P400.
+ * [linux-perf](https://ci.chromium.org/p/chrome/builders/ci/linux-perf): Ubuntu-18.04, 8 core, NVIDIA Quadro P400.
+ * [linux-perf-rel](https://ci.chromium.org/p/chrome/builders/ci/linux-perf-rel): Ubuntu-18.04, 8 core, NVIDIA Quadro P400.
 
 ## Mac
 
diff --git a/docs/ui/android/bytecode_rewriting.md b/docs/ui/android/bytecode_rewriting.md
index 0c0a13a5..d5742eb 100644
--- a/docs/ui/android/bytecode_rewriting.md
+++ b/docs/ui/android/bytecode_rewriting.md
@@ -53,6 +53,14 @@
 [compile_java.py](https://source.chromium.org/chromium/chromium/src/+/master:build/android/gyp/compile_java.py),
 and print a message pointing users here, which is likely why you're reading this :)
 
+If you need to apply FragmentActivityReplacer to a given target then add …
+
+```
+bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer"
+```
+
+… to the build configuration for that target.
+
 ## How does this affect my code?
 
 The goal is for these changes to be as transparent as possible; most code shouldn't run into issues.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 05bf58c0..1ce66fa 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -103,10 +103,6 @@
     "content_verify_job.h",
     "crx_file_info.cc",
     "crx_file_info.h",
-    "declarative_user_script_manager.cc",
-    "declarative_user_script_manager.h",
-    "declarative_user_script_manager_factory.cc",
-    "declarative_user_script_manager_factory.h",
     "declarative_user_script_set.cc",
     "declarative_user_script_set.h",
     "deferred_start_render_host.h",
@@ -197,8 +193,6 @@
     "extension_system_provider.h",
     "extension_user_script_loader.cc",
     "extension_user_script_loader.h",
-    "extension_user_script_manager.cc",
-    "extension_user_script_manager.h",
     "extension_util.cc",
     "extension_util.h",
     "extension_web_contents_observer.cc",
@@ -365,6 +359,8 @@
     "url_request_util.h",
     "user_script_loader.cc",
     "user_script_loader.h",
+    "user_script_manager.cc",
+    "user_script_manager.h",
     "verified_contents.cc",
     "verified_contents.h",
     "view_type_utils.cc",
diff --git a/extensions/browser/api/execute_code_function.cc b/extensions/browser/api/execute_code_function.cc
index b2f05a7..27b0aa5 100644
--- a/extensions/browser/api/execute_code_function.cc
+++ b/extensions/browser/api/execute_code_function.cc
@@ -113,14 +113,14 @@
   }
   CHECK_NE(UserScript::UNDEFINED, run_at);
 
-  CSSOrigin css_origin = CSS_ORIGIN_AUTHOR;
+  CSSOrigin css_origin = CSSOrigin::kAuthor;
   switch (details_->css_origin) {
     case api::extension_types::CSS_ORIGIN_NONE:
     case api::extension_types::CSS_ORIGIN_AUTHOR:
-      css_origin = CSS_ORIGIN_AUTHOR;
+      css_origin = CSSOrigin::kAuthor;
       break;
     case api::extension_types::CSS_ORIGIN_USER:
-      css_origin = CSS_ORIGIN_USER;
+      css_origin = CSSOrigin::kUser;
       break;
   }
 
diff --git a/extensions/browser/browser_context_keyed_service_factories.cc b/extensions/browser/browser_context_keyed_service_factories.cc
index f9d0281d..143abcf 100644
--- a/extensions/browser/browser_context_keyed_service_factories.cc
+++ b/extensions/browser/browser_context_keyed_service_factories.cc
@@ -35,7 +35,6 @@
 #include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/app_window/app_window_geometry_cache.h"
 #include "extensions/browser/app_window/app_window_registry.h"
-#include "extensions/browser/declarative_user_script_manager_factory.h"
 #include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_message_filter.h"
 #include "extensions/browser/extension_prefs_factory.h"
@@ -76,7 +75,6 @@
   api::TCPSocketEventDispatcher::GetFactoryInstance();
   api::UDPSocketEventDispatcher::GetFactoryInstance();
   declarative_net_request::RulesMonitorService::GetFactoryInstance();
-  DeclarativeUserScriptManagerFactory::GetInstance();
   EnsureExtensionURLLoaderFactoryShutdownNotifierFactoryBuilt();
   EventRouterFactory::GetInstance();
   ExtensionMessageFilter::EnsureShutdownNotifierFactoryBuilt();
diff --git a/extensions/browser/declarative_user_script_manager.cc b/extensions/browser/declarative_user_script_manager.cc
deleted file mode 100644
index e4709f74..0000000
--- a/extensions/browser/declarative_user_script_manager.cc
+++ /dev/null
@@ -1,62 +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 "extensions/browser/declarative_user_script_manager.h"
-
-#include "content/public/browser/browser_context.h"
-#include "extensions/browser/declarative_user_script_manager_factory.h"
-#include "extensions/browser/declarative_user_script_set.h"
-
-namespace extensions {
-
-DeclarativeUserScriptManager::DeclarativeUserScriptManager(
-    content::BrowserContext* browser_context)
-    : browser_context_(browser_context) {
-  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context));
-}
-
-DeclarativeUserScriptManager::~DeclarativeUserScriptManager() {
-}
-
-// static
-DeclarativeUserScriptManager* DeclarativeUserScriptManager::Get(
-    content::BrowserContext* browser_context) {
-  return DeclarativeUserScriptManagerFactory::GetForBrowserContext(
-      browser_context);
-}
-
-DeclarativeUserScriptSet*
-DeclarativeUserScriptManager::GetDeclarativeUserScriptSetByID(
-    const HostID& host_id) {
-  auto it = declarative_user_script_sets_.find(host_id);
-
-  if (it != declarative_user_script_sets_.end())
-    return it->second.get();
-
-  return CreateDeclarativeUserScriptSet(host_id);
-}
-
-DeclarativeUserScriptSet*
-DeclarativeUserScriptManager::CreateDeclarativeUserScriptSet(
-    const HostID& host_id) {
-  // Inserts a new DeclarativeUserScriptManager and returns a ptr to it.
-  return declarative_user_script_sets_
-      .insert(
-          std::make_pair(host_id, std::make_unique<DeclarativeUserScriptSet>(
-                                      browser_context_, host_id)))
-      .first->second.get();
-}
-
-void DeclarativeUserScriptManager::OnExtensionUnloaded(
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    UnloadedExtensionReason reason) {
-  for (const auto& val : declarative_user_script_sets_) {
-    DeclarativeUserScriptSet* set = val.second.get();
-    if (set->host_id().id() == extension->id())
-      set->ClearScripts();
-  }
-}
-
-}  // namespace extensions
diff --git a/extensions/browser/declarative_user_script_manager.h b/extensions/browser/declarative_user_script_manager.h
deleted file mode 100644
index cbac791..0000000
--- a/extensions/browser/declarative_user_script_manager.h
+++ /dev/null
@@ -1,68 +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 EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_H_
-#define EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_H_
-
-#include <map>
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_registry_observer.h"
-#include "extensions/common/host_id.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace extensions {
-class DeclarativeUserScriptSet;
-
-// Manages a set of DeclarativeUserScriptSet objects for script injections.
-class DeclarativeUserScriptManager : public KeyedService,
-                                     public ExtensionRegistryObserver {
- public:
-  explicit DeclarativeUserScriptManager(
-      content::BrowserContext* browser_context);
-  ~DeclarativeUserScriptManager() override;
-
-  // Convenience method to return the DeclarativeUserScriptManager for a given
-  // |context|.
-  static DeclarativeUserScriptManager* Get(content::BrowserContext* context);
-
-  // Gets the user script set for declarative scripts by the given HostId.
-  // If one does not exist, a new object will be created.
-  DeclarativeUserScriptSet* GetDeclarativeUserScriptSetByID(
-      const HostID& host_id);
-
- private:
-  using UserScriptSetMap =
-      std::map<HostID, std::unique_ptr<DeclarativeUserScriptSet>>;
-
-  // ExtensionRegistryObserver:
-  void OnExtensionUnloaded(content::BrowserContext* browser_context,
-                           const Extension* extension,
-                           UnloadedExtensionReason reason) override;
-
-  // Creates a DeclarativeUserScriptSet object.
-  DeclarativeUserScriptSet* CreateDeclarativeUserScriptSet(
-      const HostID& host_id);
-
-  // A map of DeclarativeUserScriptSets for each host; each set is lazily
-  // initialized.
-  UserScriptSetMap declarative_user_script_sets_;
-
-  content::BrowserContext* browser_context_;
-
-  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
-      extension_registry_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(DeclarativeUserScriptManager);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_H_
diff --git a/extensions/browser/declarative_user_script_manager_factory.cc b/extensions/browser/declarative_user_script_manager_factory.cc
deleted file mode 100644
index cff2c69..0000000
--- a/extensions/browser/declarative_user_script_manager_factory.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/browser/declarative_user_script_manager_factory.h"
-
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/declarative_user_script_manager.h"
-#include "extensions/browser/extension_registry_factory.h"
-#include "extensions/browser/extensions_browser_client.h"
-
-using content::BrowserContext;
-
-namespace extensions {
-
-// static
-DeclarativeUserScriptManager*
-DeclarativeUserScriptManagerFactory::GetForBrowserContext(
-    BrowserContext* context) {
-  return static_cast<DeclarativeUserScriptManager*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-DeclarativeUserScriptManagerFactory*
-DeclarativeUserScriptManagerFactory::GetInstance() {
-  return base::Singleton<DeclarativeUserScriptManagerFactory>::get();
-}
-
-DeclarativeUserScriptManagerFactory::DeclarativeUserScriptManagerFactory()
-    : BrowserContextKeyedServiceFactory(
-          "DeclarativeUserScriptManager",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(ExtensionRegistryFactory::GetInstance());
-}
-
-DeclarativeUserScriptManagerFactory::~DeclarativeUserScriptManagerFactory() {
-}
-
-KeyedService* DeclarativeUserScriptManagerFactory::BuildServiceInstanceFor(
-    BrowserContext* context) const {
-  return new DeclarativeUserScriptManager(context);
-}
-
-BrowserContext* DeclarativeUserScriptManagerFactory::GetBrowserContextToUse(
-    BrowserContext* context) const {
-  // Redirected in incognito.
-  return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
-}
-
-}  // namespace extensions
diff --git a/extensions/browser/declarative_user_script_manager_factory.h b/extensions/browser/declarative_user_script_manager_factory.h
deleted file mode 100644
index 8ba68b55..0000000
--- a/extensions/browser/declarative_user_script_manager_factory.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_FACTORY_H_
-#define EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace extensions {
-
-class DeclarativeUserScriptManager;
-
-class DeclarativeUserScriptManagerFactory
-    : public BrowserContextKeyedServiceFactory {
- public:
-  static DeclarativeUserScriptManager* GetForBrowserContext(
-      content::BrowserContext* context);
-  static DeclarativeUserScriptManagerFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<
-      DeclarativeUserScriptManagerFactory>;
-
-  DeclarativeUserScriptManagerFactory();
-  ~DeclarativeUserScriptManagerFactory() override;
-
-  // BrowserContextKeyedServiceFactory implementation
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(DeclarativeUserScriptManagerFactory);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_BROWSER_DECLARATIVE_USER_SCRIPT_MANAGER_FACTORY_H_
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 9b1f45f..c24eefac 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1602,6 +1602,7 @@
   FILEMANAGERPRIVATE_ISTABLETMODEENABLED = 1539,
   FILEMANAGERPRIVATE_NOTIFYDRIVEDIALOGRESULT = 1540,
   ENTERPRISEREPORTINGPRIVATE_GETCONTEXTINFO = 1541,
+  SCRIPTING_REMOVECSS = 1542,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_system.h b/extensions/browser/extension_system.h
index efce592..1d247c8 100644
--- a/extensions/browser/extension_system.h
+++ b/extensions/browser/extension_system.h
@@ -40,8 +40,8 @@
 class QuotaService;
 class RuntimeData;
 class ServiceWorkerManager;
-class ExtensionUserScriptManager;
 class StateStore;
+class UserScriptManager;
 class ValueStoreFactory;
 enum class UnloadedExtensionReason;
 
@@ -85,8 +85,8 @@
   // The ServiceWorkerManager is created at startup.
   virtual ServiceWorkerManager* service_worker_manager() = 0;
 
-  // The ExtensionUserScriptManager is created at startup.
-  virtual ExtensionUserScriptManager* extension_user_script_manager() = 0;
+  // The UserScriptManager is created at startup.
+  virtual UserScriptManager* user_script_manager() = 0;
 
   // The StateStore is created at startup.
   virtual StateStore* state_store() = 0;
diff --git a/extensions/browser/extension_user_script_loader.h b/extensions/browser/extension_user_script_loader.h
index 054b10f..0097294 100644
--- a/extensions/browser/extension_user_script_loader.h
+++ b/extensions/browser/extension_user_script_loader.h
@@ -32,7 +32,7 @@
   using HostsInfo = std::map<HostID, PathAndLocaleInfo>;
 
   // The listen_for_extension_system_loaded is only set true when initializing
-  // the Extension System, e.g, when constructs ExtensionUserScriptManager in
+  // the Extension System, e.g, when constructs UserScriptManager in
   // ExtensionSystemImpl.
   ExtensionUserScriptLoader(content::BrowserContext* browser_context,
                             const HostID& host_id,
diff --git a/extensions/browser/extension_user_script_manager.cc b/extensions/browser/extension_user_script_manager.cc
deleted file mode 100644
index 05d03f3..0000000
--- a/extensions/browser/extension_user_script_manager.cc
+++ /dev/null
@@ -1,59 +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 "extensions/browser/extension_user_script_manager.h"
-
-#include "extensions/browser/extension_util.h"
-#include "extensions/common/host_id.h"
-#include "extensions/common/manifest_handlers/content_scripts_handler.h"
-
-namespace extensions {
-
-ExtensionUserScriptManager::ExtensionUserScriptManager(
-    content::BrowserContext* browser_context)
-    : loader_(browser_context,
-              HostID(),
-              true /* listen_for_extension_system_loaded */),
-      browser_context_(browser_context) {
-  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
-}
-
-ExtensionUserScriptManager::~ExtensionUserScriptManager() {}
-
-void ExtensionUserScriptManager::OnExtensionLoaded(
-    content::BrowserContext* browser_context,
-    const Extension* extension) {
-  loader_.AddScripts(GetScriptsMetadata(extension));
-}
-
-void ExtensionUserScriptManager::OnExtensionUnloaded(
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    UnloadedExtensionReason reason) {
-  const UserScriptList& script_list =
-      ContentScriptsInfo::GetContentScripts(extension);
-  std::set<UserScriptIDPair> scripts_to_remove;
-  for (const std::unique_ptr<UserScript>& script : script_list)
-    scripts_to_remove.insert(UserScriptIDPair(script->id(), script->host_id()));
-  loader_.RemoveScripts(scripts_to_remove);
-}
-
-std::unique_ptr<UserScriptList> ExtensionUserScriptManager::GetScriptsMetadata(
-    const Extension* extension) {
-  bool incognito_enabled =
-      util::IsIncognitoEnabled(extension->id(), browser_context_);
-  const UserScriptList& script_list =
-      ContentScriptsInfo::GetContentScripts(extension);
-  std::unique_ptr<UserScriptList> script_vector(new UserScriptList());
-  script_vector->reserve(script_list.size());
-  for (const std::unique_ptr<UserScript>& script : script_list) {
-    std::unique_ptr<UserScript> script_copy =
-        UserScript::CopyMetadataFrom(*script);
-    script_copy->set_incognito_enabled(incognito_enabled);
-    script_vector->push_back(std::move(script_copy));
-  }
-  return script_vector;
-}
-
-}  // namespace extensions
diff --git a/extensions/browser/extension_user_script_manager.h b/extensions/browser/extension_user_script_manager.h
deleted file mode 100644
index 4e3eaee..0000000
--- a/extensions/browser/extension_user_script_manager.h
+++ /dev/null
@@ -1,64 +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 EXTENSIONS_BROWSER_EXTENSION_USER_SCRIPT_MANAGER_H_
-#define EXTENSIONS_BROWSER_EXTENSION_USER_SCRIPT_MANAGER_H_
-
-#include <memory>
-#include <set>
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_registry_observer.h"
-#include "extensions/browser/extension_user_script_loader.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/user_script.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace extensions {
-
-// Manages statically-defined user scripts for all extensions. Owns a
-// UserScriptLoader to which file loading and shared memory management
-// operations are delegated.
-// TODO(crbug.com/1168627): Manage declarative user scripts (from API/webview)
-// from all extensions in this class.
-class ExtensionUserScriptManager : public ExtensionRegistryObserver {
- public:
-  explicit ExtensionUserScriptManager(content::BrowserContext* browser_context);
-  ~ExtensionUserScriptManager() override;
-
-  UserScriptLoader* script_loader() { return &loader_; }
-
- private:
-  // ExtensionRegistryObserver implementation.
-  void OnExtensionLoaded(content::BrowserContext* browser_context,
-                         const Extension* extension) override;
-  void OnExtensionUnloaded(content::BrowserContext* browser_context,
-                           const Extension* extension,
-                           UnloadedExtensionReason reason) override;
-
-  // Gets an extension's scripts' metadata; i.e., gets a list of UserScript
-  // objects that contains script info, but not the contents of the scripts.
-  std::unique_ptr<UserScriptList> GetScriptsMetadata(
-      const Extension* extension);
-
-  // Script loader that handles loading contents of scripts into shared memory
-  // and notifying renderers of scripts in shared memory.
-  ExtensionUserScriptLoader loader_;
-
-  content::BrowserContext* browser_context_;
-
-  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
-      extension_registry_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionUserScriptManager);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_BROWSER_EXTENSION_USER_SCRIPT_MANAGER_H_
diff --git a/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc b/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
index 10ec9b0..6f696b7 100644
--- a/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
+++ b/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
@@ -13,11 +13,11 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
-#include "extensions/browser/declarative_user_script_manager.h"
 #include "extensions/browser/declarative_user_script_set.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
+#include "extensions/browser/user_script_manager.h"
 
 using content::BrowserThread;
 
@@ -54,7 +54,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DeclarativeUserScriptSet* script_set =
-      DeclarativeUserScriptManager::Get(browser_context_)
+      ExtensionSystem::Get(browser_context_)
+          ->user_script_manager()
           ->GetDeclarativeUserScriptSetByID(host_id);
   DCHECK(script_set);
 
@@ -147,7 +148,8 @@
     return;
 
   DeclarativeUserScriptSet* script_set =
-      DeclarativeUserScriptManager::Get(browser_context_)
+      ExtensionSystem::Get(browser_context_)
+          ->user_script_manager()
           ->GetDeclarativeUserScriptSetByID(host_id);
   CHECK(script_set);
 
diff --git a/extensions/browser/mock_extension_system.cc b/extensions/browser/mock_extension_system.cc
index 7e90ac1..b01b336 100644
--- a/extensions/browser/mock_extension_system.cc
+++ b/extensions/browser/mock_extension_system.cc
@@ -37,8 +37,7 @@
   return nullptr;
 }
 
-ExtensionUserScriptManager*
-MockExtensionSystem::extension_user_script_manager() {
+UserScriptManager* MockExtensionSystem::user_script_manager() {
   return nullptr;
 }
 
diff --git a/extensions/browser/mock_extension_system.h b/extensions/browser/mock_extension_system.h
index 137e0f2a..5f251675 100644
--- a/extensions/browser/mock_extension_system.h
+++ b/extensions/browser/mock_extension_system.h
@@ -36,7 +36,7 @@
   RuntimeData* runtime_data() override;
   ManagementPolicy* management_policy() override;
   ServiceWorkerManager* service_worker_manager() override;
-  ExtensionUserScriptManager* extension_user_script_manager() override;
+  UserScriptManager* user_script_manager() override;
   StateStore* state_store() override;
   StateStore* rules_store() override;
   scoped_refptr<ValueStoreFactory> store_factory() override;
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index e50ed7c..d788bd26 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -145,13 +145,10 @@
 
   // Load default policy_blocked_hosts and policy_allowed_hosts settings, part
   // of the ExtensionSettings policy.
-  ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params params;
   int context_id = util::GetBrowserContextId(renderer_context);
-  params.default_policy_blocked_hosts =
-      PermissionsData::GetDefaultPolicyBlockedHosts(context_id);
-  params.default_policy_allowed_hosts =
-      PermissionsData::GetDefaultPolicyAllowedHosts(context_id);
-  process->Send(new ExtensionMsg_UpdateDefaultPolicyHostRestrictions(params));
+  renderer->UpdateDefaultPolicyHostRestrictions(
+      PermissionsData::GetDefaultPolicyBlockedHosts(context_id),
+      PermissionsData::GetDefaultPolicyAllowedHosts(context_id));
 
   // Loaded extensions.
   std::vector<ExtensionMsg_Loaded_Params> loaded_extensions;
diff --git a/extensions/browser/renderer_startup_helper_unittest.cc b/extensions/browser/renderer_startup_helper_unittest.cc
index 9df5479..7dd4570 100644
--- a/extensions/browser/renderer_startup_helper_unittest.cc
+++ b/extensions/browser/renderer_startup_helper_unittest.cc
@@ -15,6 +15,7 @@
 #include "extensions/browser/test_extensions_browser_client.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/extension_messages.h"
+#include "extensions/common/permissions/permissions_data.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 
 namespace extensions {
@@ -32,6 +33,14 @@
 
   size_t num_unloaded_extensions() { return unloaded_extensions_.size(); }
 
+  const URLPatternSet& default_policy_blocked_hosts() const {
+    return default_blocked_hosts_;
+  }
+
+  const URLPatternSet& default_policy_allowed_hosts() const {
+    return default_allowed_hosts_;
+  }
+
  protected:
   mojo::PendingAssociatedRemote<mojom::Renderer> BindNewRendererRemote(
       content::RenderProcessHost* process) override {
@@ -62,6 +71,15 @@
   void SetScriptingAllowlist(
       const std::vector<std::string>& extension_ids) override {}
 
+  void UpdateDefaultPolicyHostRestrictions(
+      const URLPatternSet& default_policy_blocked_hosts,
+      const URLPatternSet& default_policy_allowed_hosts) override {
+    default_blocked_hosts_.AddPatterns(default_policy_blocked_hosts);
+    default_allowed_hosts_.AddPatterns(default_policy_allowed_hosts);
+  }
+
+  URLPatternSet default_blocked_hosts_;
+  URLPatternSet default_allowed_hosts_;
   std::vector<std::string> activated_extensions_;
   std::vector<std::string> unloaded_extensions_;
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
@@ -272,11 +290,32 @@
   EXPECT_TRUE(IsExtensionPendingActivationInProcess(
       *extension_, render_process_host_.get()));
 
+  // Initialize PermissionsData default policy hosts restrictions.
+  // During the process initialization, UpdateDefaultPolicyHostRestrictions
+  // will be called with the default policy values returned by PermissionsData.
+  URLPatternSet default_blocked_hosts;
+  URLPatternSet default_allowed_hosts;
+  default_blocked_hosts.AddPattern(
+      URLPattern(URLPattern::SCHEME_ALL, "*://*.example.com/*"));
+  default_allowed_hosts.AddPattern(
+      URLPattern(URLPattern::SCHEME_ALL, "*://test.example2.com/*"));
+  PermissionsData::SetDefaultPolicyHostRestrictions(
+      util::GetBrowserContextId(browser_context()), default_blocked_hosts,
+      default_allowed_hosts);
+
   // Initialize the render process.
   SimulateRenderProcessCreated(render_process_host_.get());
   // The renderer would have been sent multiple initialization messages
   // including the loading and activation messages for the extension.
-  EXPECT_LE(2u, sink.message_count());
+  EXPECT_LE(1u, sink.message_count());
+
+  // Method UpdateDefaultPolicyHostRestrictions() from mojom::Renderer should
+  // have been called with the default policy for blocked/allowed hosts given by
+  // PermissionsData, which was initialized above.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(default_blocked_hosts, helper_->default_policy_blocked_hosts());
+  EXPECT_EQ(default_allowed_hosts, helper_->default_policy_allowed_hosts());
+
   EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
   EXPECT_TRUE(
       IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
diff --git a/extensions/browser/user_script_manager.cc b/extensions/browser/user_script_manager.cc
new file mode 100644
index 0000000..ced5f5e
--- /dev/null
+++ b/extensions/browser/user_script_manager.cc
@@ -0,0 +1,83 @@
+// Copyright 2021 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 "extensions/browser/user_script_manager.h"
+
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/declarative_user_script_set.h"
+#include "extensions/browser/extension_util.h"
+#include "extensions/common/manifest_handlers/content_scripts_handler.h"
+
+namespace extensions {
+
+UserScriptManager::UserScriptManager(content::BrowserContext* browser_context)
+    : manifest_script_loader_(browser_context,
+                              HostID(),
+                              true /* listen_for_extension_system_loaded */),
+      browser_context_(browser_context) {
+  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
+}
+
+UserScriptManager::~UserScriptManager() = default;
+
+DeclarativeUserScriptSet* UserScriptManager::GetDeclarativeUserScriptSetByID(
+    const HostID& host_id) {
+  auto it = declarative_user_script_sets_.find(host_id);
+
+  if (it != declarative_user_script_sets_.end())
+    return it->second.get();
+
+  return CreateDeclarativeUserScriptSet(host_id);
+}
+
+void UserScriptManager::OnExtensionLoaded(
+    content::BrowserContext* browser_context,
+    const Extension* extension) {
+  manifest_script_loader_.AddScripts(GetManifestScriptsMetadata(extension));
+}
+
+void UserScriptManager::OnExtensionUnloaded(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    UnloadedExtensionReason reason) {
+  const UserScriptList& script_list =
+      ContentScriptsInfo::GetContentScripts(extension);
+  std::set<UserScriptIDPair> scripts_to_remove;
+  for (const std::unique_ptr<UserScript>& script : script_list)
+    scripts_to_remove.insert(UserScriptIDPair(script->id(), script->host_id()));
+  manifest_script_loader_.RemoveScripts(scripts_to_remove);
+
+  auto it = declarative_user_script_sets_.find(
+      HostID(HostID::EXTENSIONS, extension->id()));
+  if (it != declarative_user_script_sets_.end())
+    it->second->ClearScripts();
+}
+
+std::unique_ptr<UserScriptList> UserScriptManager::GetManifestScriptsMetadata(
+    const Extension* extension) {
+  bool incognito_enabled =
+      util::IsIncognitoEnabled(extension->id(), browser_context_);
+  const UserScriptList& script_list =
+      ContentScriptsInfo::GetContentScripts(extension);
+  auto script_vector = std::make_unique<UserScriptList>();
+  script_vector->reserve(script_list.size());
+  for (const auto& script : script_list) {
+    std::unique_ptr<UserScript> script_copy =
+        UserScript::CopyMetadataFrom(*script);
+    script_copy->set_incognito_enabled(incognito_enabled);
+    script_vector->push_back(std::move(script_copy));
+  }
+  return script_vector;
+}
+
+DeclarativeUserScriptSet* UserScriptManager::CreateDeclarativeUserScriptSet(
+    const HostID& host_id) {
+  // Inserts a new DeclarativeUserScriptSet and returns a ptr to it.
+  return declarative_user_script_sets_
+      .emplace(host_id, std::make_unique<DeclarativeUserScriptSet>(
+                            browser_context_, host_id))
+      .first->second.get();
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/user_script_manager.h b/extensions/browser/user_script_manager.h
new file mode 100644
index 0000000..dac6d83
--- /dev/null
+++ b/extensions/browser/user_script_manager.h
@@ -0,0 +1,89 @@
+// Copyright 2021 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 EXTENSIONS_BROWSER_USER_SCRIPT_MANAGER_H_
+#define EXTENSIONS_BROWSER_USER_SCRIPT_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/scoped_observer.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/browser/extension_user_script_loader.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/host_id.h"
+#include "extensions/common/user_script.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+class DeclarativeUserScriptSet;
+
+// Manages user scripts for all extensions and webview scripts from WebUI pages.
+// Owns one UserScriptLoader for manifest extension scripts, and a map of HostID
+// to UserScriptLoaders for declarative extension and WebUI scripts. File
+// loading and shared memory management operations are delegated to these
+// UserScriptLoaders.
+class UserScriptManager : public ExtensionRegistryObserver {
+ public:
+  explicit UserScriptManager(content::BrowserContext* browser_context);
+  ~UserScriptManager() override;
+  UserScriptManager(const UserScriptManager& other) = delete;
+  UserScriptManager& operator=(const UserScriptManager& other) = delete;
+
+  UserScriptLoader* manifest_script_loader() {
+    return &manifest_script_loader_;
+  }
+
+  // Gets the user script set for declarative scripts by the given HostID.
+  // If one does not exist, a new object will be created.
+  DeclarativeUserScriptSet* GetDeclarativeUserScriptSetByID(
+      const HostID& host_id);
+
+ private:
+  using UserScriptSetMap =
+      std::map<HostID, std::unique_ptr<DeclarativeUserScriptSet>>;
+
+  // ExtensionRegistryObserver implementation.
+  void OnExtensionLoaded(content::BrowserContext* browser_context,
+                         const Extension* extension) override;
+  void OnExtensionUnloaded(content::BrowserContext* browser_context,
+                           const Extension* extension,
+                           UnloadedExtensionReason reason) override;
+
+  // Gets an extension's manifest scripts' metadata; i.e., gets a list of
+  // UserScript objects that contains script info, but not the contents of the
+  // scripts.
+  std::unique_ptr<UserScriptList> GetManifestScriptsMetadata(
+      const Extension* extension);
+
+  // Creates a DeclarativeUserScriptSet object.
+  DeclarativeUserScriptSet* CreateDeclarativeUserScriptSet(
+      const HostID& host_id);
+
+  // A map of DeclarativeUserScriptSets for each host. Each set is lazily
+  // initialized, and contains scripts from APIs for an extension host, or
+  // webview scripts for a WebUI host.
+  // TODO(crbug.com/1168627): Have one UserScriptLoader per extension, and split
+  // WebUI loaders into another set.
+  UserScriptSetMap declarative_user_script_sets_;
+
+  // Script loader for manifest extension scripts that handles loading contents
+  // of scripts into shared memory and notifying renderers of scripts in shared
+  // memory.
+  ExtensionUserScriptLoader manifest_script_loader_;
+
+  content::BrowserContext* const browser_context_;
+
+  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
+      extension_registry_observer_{this};
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_USER_SCRIPT_MANAGER_H_
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 822c013..b1ec6fc 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -46,6 +46,7 @@
       "mojom/guest_view.mojom",
       "mojom/keep_alive.mojom",
       "mojom/renderer.mojom",
+      "mojom/url_pattern_set.mojom",
     ]
 
     public_deps = [
@@ -67,6 +68,22 @@
         traits_headers = [ "//extensions/common/mojom/channel_mojom_traits.h" ]
         traits_public_deps = [ "//components/version_info:channel" ]
       },
+      {
+        types = [
+          {
+            mojom = "extensions.mojom.URLPattern"
+            cpp = "::URLPattern"
+          },
+          {
+            mojom = "extensions.mojom.URLPatternSet"
+            cpp = "::extensions::URLPatternSet"
+          },
+        ]
+        traits_headers =
+            [ "//extensions/common/mojom/url_pattern_set_mojom_traits.h" ]
+        traits_sources =
+            [ "//extensions/common/mojom/url_pattern_set_mojom_traits.cc" ]
+      },
     ]
     overridden_deps = [ "//content/public/common:interfaces" ]
 
@@ -473,6 +490,7 @@
       "manifest_handlers/web_app_shortcut_icons_handler_unittest.cc",
       "manifest_unittest.cc",
       "message_bundle_unittest.cc",
+      "mojom/url_pattern_set_mojom_traits_unittest.cc",
       "permissions/api_permission_set_unittest.cc",
       "permissions/api_permission_unittest.cc",
       "permissions/base_set_operators_unittest.cc",
@@ -499,6 +517,7 @@
       "//extensions:extensions_resources",
       "//extensions/common:mojom",
       "//extensions/common/api",
+      "//mojo/public/cpp/test_support:test_utils",
       "//tools/json_schema_compiler:generated_api_util",
 
       # TODO(brettw) these tests should not be including headers from browser.
diff --git a/extensions/common/constants.h b/extensions/common/constants.h
index a7df654a..f1f7fa4 100644
--- a/extensions/common/constants.h
+++ b/extensions/common/constants.h
@@ -146,8 +146,11 @@
 };
 
 // The origin of injected CSS.
-enum CSSOrigin { CSS_ORIGIN_AUTHOR, CSS_ORIGIN_USER };
-static const CSSOrigin CSS_ORIGIN_LAST = CSS_ORIGIN_USER;
+enum class CSSOrigin {
+  kAuthor = 0,
+  kUser = 1,
+  kLast = kUser,
+};
 
 }  // namespace extensions
 
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index ab8d0c0..d549efb 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -51,7 +51,7 @@
 
 #define IPC_MESSAGE_START ExtensionMsgStart
 
-IPC_ENUM_TRAITS_MAX_VALUE(extensions::CSSOrigin, extensions::CSS_ORIGIN_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(extensions::CSSOrigin, extensions::CSSOrigin::kLast)
 
 IPC_ENUM_TRAITS_MAX_VALUE(extensions::ViewType, extensions::VIEW_TYPE_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(content::SocketPermissionRequest::OperationType,
@@ -527,12 +527,6 @@
   IPC_STRUCT_MEMBER(bool, uses_default_policy_host_restrictions)
 IPC_STRUCT_END()
 
-// Parameters structure for ExtensionMsg_UpdateDefaultPolicyHostRestrictions.
-IPC_STRUCT_BEGIN(ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params)
-  IPC_STRUCT_MEMBER(extensions::URLPatternSet, default_policy_blocked_hosts)
-  IPC_STRUCT_MEMBER(extensions::URLPatternSet, default_policy_allowed_hosts)
-IPC_STRUCT_END()
-
 // Messages sent from the browser to the renderer:
 
 // The browser sends this message in response to all extension api calls. The
@@ -612,10 +606,6 @@
 IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdatePermissions,
                      ExtensionMsg_UpdatePermissions_Params)
 
-// Tell the renderer to update an extension's policy_blocked_hosts set.
-IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateDefaultPolicyHostRestrictions,
-                     ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params)
-
 // Tell the render view about new tab-specific permissions for an extension.
 IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdateTabSpecificPermissions,
                      GURL /* url */,
diff --git a/extensions/common/extension_resource.cc b/extensions/common/extension_resource.cc
index fe7bcfe..a997a63b25 100644
--- a/extensions/common/extension_resource.cc
+++ b/extensions/common/extension_resource.cc
@@ -4,29 +4,27 @@
 
 #include "extensions/common/extension_resource.h"
 
-#include <stddef.h>
-
 #include "base/check.h"
 #include "base/files/file_util.h"
-#include "base/threading/thread_restrictions.h"
 
 namespace extensions {
 
-ExtensionResource::ExtensionResource() : follow_symlinks_anywhere_(false) {
-}
+ExtensionResource::ExtensionResource() : follow_symlinks_anywhere_(false) {}
 
-ExtensionResource::ExtensionResource(const std::string& extension_id,
+ExtensionResource::ExtensionResource(const ExtensionId& extension_id,
                                      const base::FilePath& extension_root,
                                      const base::FilePath& relative_path)
     : extension_id_(extension_id),
       extension_root_(extension_root),
       relative_path_(relative_path),
-      follow_symlinks_anywhere_(false) {
-}
+      follow_symlinks_anywhere_(false) {}
 
 ExtensionResource::ExtensionResource(const ExtensionResource& other) = default;
+ExtensionResource::ExtensionResource(ExtensionResource&& other) = default;
+ExtensionResource& ExtensionResource::operator=(ExtensionResource&& other) =
+    default;
 
-ExtensionResource::~ExtensionResource() {}
+ExtensionResource::~ExtensionResource() = default;
 
 void ExtensionResource::set_follow_symlinks_anywhere() {
   follow_symlinks_anywhere_ = true;
@@ -101,32 +99,4 @@
   return base::FilePath();
 }
 
-// Unit-testing helpers.
-base::FilePath::StringType ExtensionResource::NormalizeSeperators(
-    const base::FilePath::StringType& path) const {
-#if defined(FILE_PATH_USES_WIN_SEPARATORS)
-  base::FilePath::StringType win_path = path;
-  for (size_t i = 0; i < win_path.length(); i++) {
-    if (base::FilePath::IsSeparator(win_path[i]))
-      win_path[i] = base::FilePath::kSeparators[0];
-  }
-  return win_path;
-#else
-  return path;
-#endif  // FILE_PATH_USES_WIN_SEPARATORS
-}
-
-bool ExtensionResource::ComparePathWithDefault(
-    const base::FilePath& path) const {
-  // Make sure we have a cached value to test against...
-  if (full_resource_path_.empty())
-    GetFilePath();
-  if (NormalizeSeperators(path.value()) ==
-    NormalizeSeperators(full_resource_path_.value())) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
 }  // namespace extensions
diff --git a/extensions/common/extension_resource.h b/extensions/common/extension_resource.h
index 1ed42a1..260d4fd9 100644
--- a/extensions/common/extension_resource.h
+++ b/extensions/common/extension_resource.h
@@ -5,9 +5,8 @@
 #ifndef EXTENSIONS_COMMON_EXTENSION_RESOURCE_H_
 #define EXTENSIONS_COMMON_EXTENSION_RESOURCE_H_
 
-#include <string>
-
 #include "base/files/file_path.h"
+#include "extensions/common/extension_id.h"
 
 namespace extensions {
 
@@ -26,12 +25,12 @@
   };
 
   ExtensionResource();
-
-  ExtensionResource(const std::string& extension_id,
+  ExtensionResource(const ExtensionId& extension_id,
                     const base::FilePath& extension_root,
                     const base::FilePath& relative_path);
-
   ExtensionResource(const ExtensionResource& other);
+  ExtensionResource(ExtensionResource&& other);
+  ExtensionResource& operator=(ExtensionResource&& other);
 
   ~ExtensionResource();
 
@@ -58,7 +57,7 @@
                                     SymlinkPolicy symlink_policy);
 
   // Getters
-  const std::string& extension_id() const { return extension_id_; }
+  const ExtensionId& extension_id() const { return extension_id_; }
 
   // Note that this might be empty for a valid ExtensionResource since dummy
   // Extension objects may be created with an empty extension root path in code.
@@ -66,16 +65,11 @@
 
   const base::FilePath& relative_path() const { return relative_path_; }
 
-  bool empty() const { return relative_path().empty(); }
-
-  // Unit test helpers.
-  base::FilePath::StringType NormalizeSeperators(
-      const base::FilePath::StringType& path) const;
-  bool ComparePathWithDefault(const base::FilePath& path) const;
+  bool empty() const { return relative_path_.empty(); }
 
  private:
   // The id of the extension that this resource is associated with.
-  std::string extension_id_;
+  ExtensionId extension_id_;
 
   // Extension root.
   base::FilePath extension_root_;
diff --git a/extensions/common/mojom/renderer.mojom b/extensions/common/mojom/renderer.mojom
index f6e0c49..754c5ff 100644
--- a/extensions/common/mojom/renderer.mojom
+++ b/extensions/common/mojom/renderer.mojom
@@ -6,6 +6,7 @@
 
 import "extensions/common/mojom/channel.mojom";
 import "extensions/common/mojom/feature_session_type.mojom";
+import "extensions/common/mojom/url_pattern_set.mojom";
 
 // This should be used for implementing browser-to-renderer control messages
 // which need to retain FIFO with respect to other mojo interfaces like
@@ -40,4 +41,9 @@
   // Updates the scripting allowlist for extensions in the render process. This
   // is only used for testing.
   SetScriptingAllowlist(array<string> extension_ids);
+
+  // Tells the renderer to update an extension's policy_blocked_hosts set.
+  UpdateDefaultPolicyHostRestrictions(
+        URLPatternSet default_policy_blocked_hosts,
+        URLPatternSet default_policy_allowed_hosts);
 };
diff --git a/extensions/common/mojom/url_pattern_set.mojom b/extensions/common/mojom/url_pattern_set.mojom
new file mode 100644
index 0000000..5b09ae9d
--- /dev/null
+++ b/extensions/common/mojom/url_pattern_set.mojom
@@ -0,0 +1,21 @@
+// Copyright 2021 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 extensions.mojom;
+
+// A pattern that can be used to match URLs. See C++ type URLPattern for full
+// documentation.
+struct URLPattern {
+  // A bitmask containing the schemes considered valid for this pattern.
+  int32 valid_schemes;
+  // A string representing this URLPattern.
+  string pattern;
+};
+
+// A set of URLs an extension uses for web content. See
+// extensions::URLPatternSet for full documentation.
+struct URLPatternSet {
+  array <URLPattern> patterns;
+};
+
diff --git a/extensions/common/mojom/url_pattern_set_mojom_traits.cc b/extensions/common/mojom/url_pattern_set_mojom_traits.cc
new file mode 100644
index 0000000..1aa3369
--- /dev/null
+++ b/extensions/common/mojom/url_pattern_set_mojom_traits.cc
@@ -0,0 +1,45 @@
+// Copyright 2021 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 "extensions/common/mojom/url_pattern_set_mojom_traits.h"
+
+namespace mojo {
+
+bool StructTraits<extensions::mojom::URLPatternDataView, URLPattern>::Read(
+    extensions::mojom::URLPatternDataView data,
+    URLPattern* out) {
+  std::string pattern;
+
+  if (!data.ReadPattern(&pattern))
+    return false;
+
+  // TODO(jstritar): We don't want the URLPattern to fail parsing when the
+  // scheme is invalid. Instead, the pattern should parse but it should not
+  // match the invalid patterns. We get around this by setting the valid
+  // schemes after parsing the pattern. Update these method calls once we can
+  // ignore scheme validation with URLPattern parse options. crbug.com/90544
+  out->SetValidSchemes(URLPattern::SCHEME_ALL);
+  URLPattern::ParseResult result = out->Parse(pattern);
+  out->SetValidSchemes(data.valid_schemes());
+
+  return URLPattern::ParseResult::kSuccess == result;
+}
+
+bool StructTraits<extensions::mojom::URLPatternSetDataView,
+                  extensions::URLPatternSet>::
+    Read(extensions::mojom::URLPatternSetDataView data,
+         extensions::URLPatternSet* out) {
+  std::vector<URLPattern> mojo_patterns;
+  if (!data.ReadPatterns(&mojo_patterns))
+    return false;
+  for (const auto& pattern : mojo_patterns)
+    out->AddPattern(pattern);
+
+  if (mojo_patterns.size() != out->patterns().size())
+    return false;
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/extensions/common/mojom/url_pattern_set_mojom_traits.h b/extensions/common/mojom/url_pattern_set_mojom_traits.h
new file mode 100644
index 0000000..c4bfeb32
--- /dev/null
+++ b/extensions/common/mojom/url_pattern_set_mojom_traits.h
@@ -0,0 +1,42 @@
+// Copyright 2021 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 EXTENSIONS_COMMON_MOJOM_URL_PATTERN_SET_MOJOM_TRAITS_H_
+#define EXTENSIONS_COMMON_MOJOM_URL_PATTERN_SET_MOJOM_TRAITS_H_
+
+#include "extensions/common/mojom/url_pattern_set.mojom-shared.h"
+#include "extensions/common/url_pattern.h"
+#include "extensions/common/url_pattern_set.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<extensions::mojom::URLPatternDataView, URLPattern> {
+  static int32_t valid_schemes(const URLPattern& pattern) {
+    return pattern.valid_schemes();
+  }
+
+  static const std::string& pattern(const URLPattern& pattern) {
+    return pattern.GetAsString();
+  }
+
+  static bool Read(extensions::mojom::URLPatternDataView data, URLPattern* out);
+};
+
+template <>
+struct StructTraits<extensions::mojom::URLPatternSetDataView,
+                    extensions::URLPatternSet> {
+  static const std::set<URLPattern>& patterns(
+      const extensions::URLPatternSet& set) {
+    return set.patterns();
+  }
+
+  static bool Read(extensions::mojom::URLPatternSetDataView data,
+                   extensions::URLPatternSet* out);
+};
+
+}  // namespace mojo
+
+#endif  // EXTENSIONS_COMMON_MOJOM_URL_PATTERN_SET_MOJOM_TRAITS_H_
diff --git a/extensions/common/mojom/url_pattern_set_mojom_traits_unittest.cc b/extensions/common/mojom/url_pattern_set_mojom_traits_unittest.cc
new file mode 100644
index 0000000..736c0e0
--- /dev/null
+++ b/extensions/common/mojom/url_pattern_set_mojom_traits_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2021 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 "extensions/common/mojom/url_pattern_set_mojom_traits.h"
+
+#include "extensions/common/mojom/url_pattern_set.mojom.h"
+#include "extensions/common/url_pattern.h"
+#include "extensions/common/url_pattern_set.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mojo::test::SerializeAndDeserialize;
+
+TEST(URLPatternSetMojomTraitsTest, BasicURLPattern) {
+  URLPattern input(URLPattern::SCHEME_HTTP);
+  EXPECT_EQ(URLPattern::ParseResult::kSuccess,
+            input.Parse("http://*.foo:1234/bar"))
+      << "Got unexpected error in the URLPattern parsing";
+
+  URLPattern output;
+  EXPECT_TRUE(
+      SerializeAndDeserialize<extensions::mojom::URLPattern>(input, output));
+  EXPECT_EQ(input, output);
+  EXPECT_EQ(input.valid_schemes(), output.valid_schemes());
+  EXPECT_EQ(input.scheme(), output.scheme());
+  EXPECT_EQ(input.host(), output.host());
+  EXPECT_EQ(input.port(), output.port());
+  EXPECT_EQ(input.path(), output.path());
+  EXPECT_EQ(input.match_all_urls(), output.match_all_urls());
+  EXPECT_EQ(input.match_subdomains(), output.match_subdomains());
+  EXPECT_EQ(input.GetAsString(), output.GetAsString());
+}
+
+TEST(URLPatternSetMojomTraitsTest, EmptyURLPatternSet) {
+  extensions::URLPatternSet input;
+  extensions::URLPatternSet output;
+
+  EXPECT_TRUE(
+      SerializeAndDeserialize<extensions::mojom::URLPatternSet>(input, output));
+  EXPECT_TRUE(output.is_empty());
+}
+
+TEST(URLPatternSetMojomTraitsTest, BasicURLPatternSet) {
+  URLPattern pattern1(URLPattern::SCHEME_ALL);
+  EXPECT_EQ(URLPattern::ParseResult::kSuccess,
+            pattern1.Parse("http://*.foo:1234/bar"))
+      << "Got unexpected error in the URLPattern parsing";
+
+  URLPattern pattern2(URLPattern::SCHEME_HTTPS);
+  EXPECT_EQ(URLPattern::ParseResult::kSuccess,
+            pattern2.Parse("https://www.google.com/foobar"))
+      << "Got unexpected error in the URLPattern parsing";
+
+  extensions::URLPatternSet input;
+  input.AddPattern(pattern1);
+  input.AddPattern(pattern2);
+
+  extensions::URLPatternSet output;
+  EXPECT_TRUE(
+      SerializeAndDeserialize<extensions::mojom::URLPatternSet>(input, output));
+  EXPECT_THAT(output.patterns(), testing::ElementsAre(pattern1, pattern2));
+}
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index c5fd996..6ee205f4b 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -878,8 +878,6 @@
   IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend)
   IPC_MESSAGE_HANDLER(ExtensionMsg_TransferBlobs, OnTransferBlobs)
   IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
-  IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateDefaultPolicyHostRestrictions,
-                      OnUpdateDefaultPolicyHostRestrictions)
   IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions,
                       OnUpdateTabSpecificPermissions)
   IPC_MESSAGE_HANDLER(ExtensionMsg_ClearTabSpecificPermissions,
@@ -1021,6 +1019,25 @@
   ExtensionsClient::Get()->SetScriptingAllowlist(extension_ids);
 }
 
+void Dispatcher::UpdateDefaultPolicyHostRestrictions(
+    const URLPatternSet& default_policy_blocked_hosts,
+    const URLPatternSet& default_policy_allowed_hosts) {
+  PermissionsData::SetDefaultPolicyHostRestrictions(
+      kRendererProfileId, default_policy_blocked_hosts,
+      default_policy_allowed_hosts);
+  // Update blink host permission allowlist exceptions for all loaded
+  // extensions.
+  for (const std::string& extension_id :
+       RendererExtensionRegistry::Get()->GetIDs()) {
+    const Extension* extension =
+        RendererExtensionRegistry::Get()->GetByID(extension_id);
+    if (extension->permissions_data()->UsesDefaultPolicyHostRestrictions()) {
+      UpdateOriginPermissions(*extension);
+    }
+  }
+  UpdateAllBindings();
+}
+
 void Dispatcher::OnCancelSuspend(const std::string& extension_id) {
   DispatchEvent(extension_id, kOnSuspendCanceledEvent, base::ListValue(),
                 nullptr);
@@ -1207,24 +1224,6 @@
   RenderThread::Get()->Send(new ExtensionHostMsg_TransferBlobsAck(blob_uuids));
 }
 
-void Dispatcher::OnUpdateDefaultPolicyHostRestrictions(
-    const ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params& params) {
-  PermissionsData::SetDefaultPolicyHostRestrictions(
-      kRendererProfileId, params.default_policy_blocked_hosts,
-      params.default_policy_allowed_hosts);
-  // Update blink host permission allowlist exceptions for all loaded
-  // extensions.
-  for (const std::string& extension_id :
-       RendererExtensionRegistry::Get()->GetIDs()) {
-    const Extension* extension =
-        RendererExtensionRegistry::Get()->GetByID(extension_id);
-    if (extension->permissions_data()->UsesDefaultPolicyHostRestrictions()) {
-      UpdateOriginPermissions(*extension);
-    }
-  }
-  UpdateAllBindings();
-}
-
 void Dispatcher::OnUpdatePermissions(
     const ExtensionMsg_UpdatePermissions_Params& params) {
   const Extension* extension =
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 4bb93df..d29ab233 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -43,7 +43,6 @@
 struct ExtensionMsg_Loaded_Params;
 struct ExtensionMsg_TabConnectionInfo;
 struct ExtensionMsg_UpdatePermissions_Params;
-struct ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params;
 
 namespace blink {
 class WebLocalFrame;
@@ -225,6 +224,10 @@
   void SetWebViewPartitionID(const std::string& partition_id) override;
   void SetScriptingAllowlist(
       const std::vector<std::string>& extension_ids) override;
+  void UpdateDefaultPolicyHostRestrictions(
+      const extensions::URLPatternSet& default_policy_blocked_hosts,
+      const extensions::URLPatternSet& default_policy_allowed_hosts) override;
+
   void OnRendererAssociatedRequest(
       mojo::PendingAssociatedReceiver<mojom::Renderer> receiver);
   void OnCancelSuspend(const std::string& extension_id);
@@ -251,8 +254,6 @@
   void OnSuspend(const std::string& extension_id);
   void OnTransferBlobs(const std::vector<std::string>& blob_uuids);
   void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params);
-  void OnUpdateDefaultPolicyHostRestrictions(
-      const ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params& params);
   void OnUpdateTabSpecificPermissions(const GURL& visible_url,
                                       const std::string& extension_id,
                                       const URLPatternSet& new_hosts,
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index 69c5b18..faeec88 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -390,10 +390,10 @@
   blink::WebDocument::CSSOrigin blink_css_origin =
       blink::WebDocument::kAuthorOrigin;
   switch (injector_->GetCssOrigin()) {
-    case CSS_ORIGIN_USER:
+    case CSSOrigin::kUser:
       blink_css_origin = blink::WebDocument::kUserOrigin;
       break;
-    case CSS_ORIGIN_AUTHOR:
+    case CSSOrigin::kAuthor:
       blink_css_origin = blink::WebDocument::kAuthorOrigin;
       break;
   }
diff --git a/extensions/renderer/user_script_injector.cc b/extensions/renderer/user_script_injector.cc
index e72761e..6f265a6 100644
--- a/extensions/renderer/user_script_injector.cc
+++ b/extensions/renderer/user_script_injector.cc
@@ -141,7 +141,7 @@
 }
 
 CSSOrigin UserScriptInjector::GetCssOrigin() const {
-  return CSS_ORIGIN_AUTHOR;
+  return CSSOrigin::kAuthor;
 }
 
 bool UserScriptInjector::IsRemovingCSS() const {
diff --git a/extensions/shell/browser/shell_extension_system.cc b/extensions/shell/browser/shell_extension_system.cc
index 54781bf5..e095c34 100644
--- a/extensions/shell/browser/shell_extension_system.cc
+++ b/extensions/shell/browser/shell_extension_system.cc
@@ -95,8 +95,7 @@
   return service_worker_manager_.get();
 }
 
-ExtensionUserScriptManager*
-ShellExtensionSystem::extension_user_script_manager() {
+UserScriptManager* ShellExtensionSystem::user_script_manager() {
   return nullptr;
 }
 
diff --git a/extensions/shell/browser/shell_extension_system.h b/extensions/shell/browser/shell_extension_system.h
index 459c97d..65037fc 100644
--- a/extensions/shell/browser/shell_extension_system.h
+++ b/extensions/shell/browser/shell_extension_system.h
@@ -64,7 +64,7 @@
   RuntimeData* runtime_data() override;
   ManagementPolicy* management_policy() override;
   ServiceWorkerManager* service_worker_manager() override;
-  ExtensionUserScriptManager* extension_user_script_manager() override;
+  UserScriptManager* user_script_manager() override;
   StateStore* state_store() override;
   StateStore* rules_store() override;
   scoped_refptr<ValueStoreFactory> store_factory() override;
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index 18f6640..c231812 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_split.h"
 #include "components/policy/content/safe_sites_navigation_throttle.h"
 #include "components/version_info/version_info.h"
+#include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/devtools_manager_delegate.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/network_service_instance.h"
@@ -199,6 +200,16 @@
       ->GetPreferredLanguages();
 }
 
+base::OnceClosure WebEngineContentBrowserClient::SelectClientCertificate(
+    content::WebContents* web_contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+  // Continue without a certificate.
+  delegate->ContinueWithCertificate(nullptr, nullptr);
+  return base::OnceClosure();
+}
+
 std::vector<std::unique_ptr<content::NavigationThrottle>>
 WebEngineContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationHandle* navigation_handle) {
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.h b/fuchsia/engine/browser/web_engine_content_browser_client.h
index 7c571cd..982fe46 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.h
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.h
@@ -52,6 +52,11 @@
                                       int child_process_id) final;
   std::string GetApplicationLocale() final;
   std::string GetAcceptLangs(content::BrowserContext* context) final;
+  base::OnceClosure SelectClientCertificate(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      net::ClientCertIdentityList client_certs,
+      std::unique_ptr<content::ClientCertificateDelegate> delegate) final;
   std::vector<std::unique_ptr<content::NavigationThrottle>>
   CreateThrottlesForNavigation(
       content::NavigationHandle* navigation_handle) final;
diff --git a/infra/config/.style.yapf b/infra/config/.style.yapf
new file mode 100644
index 0000000..b4ebbe2
--- /dev/null
+++ b/infra/config/.style.yapf
@@ -0,0 +1,6 @@
+[style]
+based_on_style = pep8
+
+# New directories should use a .style.yapf that does not include the following:
+column_limit = 80
+indent_width = 2
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index da7736c..9ee8905e 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -26599,64 +26599,6 @@
       }
     }
     builders {
-      name: "mac10.13-updater-tester-dbg"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.updater\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.resultdb.result_sink"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.gtests_local"
-        value: 100
-      }
-      experiments {
-        key: "chromium.resultdb.result_sink.junit_tests"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "luci-resultdb"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "mac10.13-updater-tester-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -26773,6 +26715,64 @@
       }
     }
     builders {
+      name: "mac10.15-updater-tester-dbg"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.updater\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.gtests_local"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "mac10.15-updater-tester-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index bb27004..db57b34 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -9918,9 +9918,9 @@
   refs: "regexp:refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbucket/luci.chromium.ci/mac10.13-updater-tester-dbg"
+    name: "buildbucket/luci.chromium.ci/mac10.15-updater-tester-dbg"
     category: "debug|mac"
-    short_name: "10.13"
+    short_name: "10.15"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/mac-updater-builder-dbg"
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 563ac39..01e08557 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -6066,20 +6066,6 @@
   }
 }
 job {
-  id: "mac10.13-updater-tester-dbg"
-  realm: "ci"
-  acls {
-    role: TRIGGERER
-    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "mac10.13-updater-tester-dbg"
-  }
-}
-job {
   id: "mac10.13-updater-tester-rel"
   realm: "ci"
   acls {
@@ -6108,6 +6094,20 @@
   }
 }
 job {
+  id: "mac10.15-updater-tester-dbg"
+  realm: "ci"
+  acls {
+    role: TRIGGERER
+    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "mac10.15-updater-tester-dbg"
+  }
+}
+job {
   id: "mac10.15-updater-tester-rel"
   realm: "ci"
   acls {
diff --git a/infra/config/scripts/.vpython3 b/infra/config/scripts/.vpython3
new file mode 100644
index 0000000..76d86bb5
--- /dev/null
+++ b/infra/config/scripts/.vpython3
@@ -0,0 +1 @@
+python_version: "3.8"
\ No newline at end of file
diff --git a/infra/config/scripts/branch-day.py b/infra/config/scripts/branch-day.py
new file mode 100755
index 0000000..8f978703
--- /dev/null
+++ b/infra/config/scripts/branch-day.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env vpython
+# Copyright 2021 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.
+"""Script for performing the branch day tasks.
+
+This script will make all of the necessary binary invocations to update
+input settings files based on flags and then re-generate the
+configuration. No output will be produced unless one of the binary
+invocations fails.
+
+Config can be updated on a new branch with:
+```
+branch-day.py --on-branch --milestone MM --branch BBBB
+```
+
+Config on trunk for enabling the new branch can be updated with:
+```
+branch-day.py --milestone MM --branch BBBB
+```
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+INFRA_CONFIG_DIR = os.path.abspath(os.path.join(__file__, '..', '..'))
+
+
+def parse_args(args=None, *, parser_type=None):
+  parser_type = parser_type or argparse.ArgumentParser
+  parser = parser_type(
+      description='Update the project settings for a chromium branch')
+  parser.set_defaults(func=_activate_milestone)
+  parser.add_argument('--milestones-py',
+                      help='Path to milestones.py script',
+                      default=os.path.join(INFRA_CONFIG_DIR, 'scripts',
+                                           'milestones.py'))
+  parser.add_argument('--branch-py',
+                      help='Path to branch.py script',
+                      default=os.path.join(INFRA_CONFIG_DIR, 'scripts',
+                                           'branch.py'))
+  parser.add_argument('--main-star',
+                      help='Path to main.star script',
+                      default=os.path.join(INFRA_CONFIG_DIR, 'main.star'))
+  parser.add_argument('--dev-star',
+                      help='Path to dev.star script',
+                      default=os.path.join(INFRA_CONFIG_DIR, 'dev.star'))
+
+  parser.add_argument(
+      '--milestone',
+      required=True,
+      help=('The milestone identifier '
+            '(e.g. the milestone number for standard release channel)'))
+  parser.add_argument(
+      '--branch',
+      required=True,
+      help='The branch name, must correspond to a ref in refs/branch-heads')
+
+  parser.add_argument(
+      '--on-branch',
+      action='store_const',
+      dest='func',
+      const=_initialize_branch,
+      help='Switches to performing the branch day tasks on the new branch')
+
+  return parser.parse_args(args)
+
+
+def _execute(cmd):
+  try:
+    subprocess.run(cmd,
+                   check=True,
+                   text=True,
+                   stdout=subprocess.PIPE,
+                   stderr=subprocess.STDOUT)
+  except subprocess.CalledProcessError as e:
+    print('Executing {} failed'.format(cmd), file=sys.stderr)
+    end = '' if e.output[-1] == '\n' else '\n'
+    print(e.output, file=sys.stderr, end=end)
+    sys.exit(1)
+
+
+def _activate_milestone(args):
+  _execute([
+      args.milestones_py, 'activate', '--milestone', args.milestone, '--branch',
+      args.branch
+  ])
+  _execute([args.main_star])
+  _execute([args.dev_star])
+
+
+def _initialize_branch(args):
+  _execute([
+      args.branch_py, 'initialize', '--milestone', args.milestone, '--branch',
+      args.branch
+  ])
+  _execute([args.main_star])
+  _execute([args.dev_star])
+
+
+def main():
+  args = parse_args()
+  args.func(args)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/infra/config/scripts/tests/branch_day_unit_test.py b/infra/config/scripts/tests/branch_day_unit_test.py
new file mode 100755
index 0000000..bec1a5b
--- /dev/null
+++ b/infra/config/scripts/tests/branch_day_unit_test.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env vpython
+# Copyright 2021 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.
+"""Integration test for branch-day.py"""
+
+import json
+import os
+import subprocess
+import tempfile
+import unittest
+
+INFRA_CONFIG_DIR = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
+BRANCH_DAY_PY = os.path.join(INFRA_CONFIG_DIR, 'scripts', 'branch-day.py')
+MOCK_PY = os.path.join(INFRA_CONFIG_DIR, 'scripts', 'tests', 'utils', 'mock.py')
+
+
+class BranchDayUnitTest(unittest.TestCase):
+  def setUp(self):
+    self._temp_dir = tempfile.TemporaryDirectory()
+    self._invocations_file = os.path.join(self._temp_dir.name,
+                                          'invocations.json')
+    self._milestones_py = os.path.join(self._temp_dir.name, 'milestones.py')
+    self._branch_py = os.path.join(self._temp_dir.name, 'branch.py')
+    self._main_star = os.path.join(self._temp_dir.name, 'main.star')
+    self._dev_star = os.path.join(self._temp_dir.name, 'dev.star')
+
+    self._binaries = (self._milestones_py, self._branch_py, self._main_star,
+                      self._dev_star)
+
+    for path in self._binaries:
+      os.symlink(MOCK_PY, path)
+
+  def tearDown(self):
+    self._temp_dir.cleanup()
+
+  def _execute_branch_day_py(self, args, mock_details=None):
+    def details(binary, stdout=None, stderr=None, exit_code=None):
+      binary = os.path.basename(binary)
+      d = {
+          'stdout': stdout or 'fake {} stdout'.format(binary),
+          'stderr': stderr or 'fake {} stderr'.format(binary),
+      }
+      if exit_code:
+        d['exit_code'] = exit_code
+      return d
+
+    mock_details = mock_details or {}
+    mock_details = {
+        b: details(b, **mock_details.get(b, {}))
+        for b in self._binaries
+    }
+
+    env = {
+        'INVOCATIONS_FILE': self._invocations_file,
+        'MOCK_DETAILS': json.dumps(mock_details),
+    }
+
+    cmd = [
+        BRANCH_DAY_PY, '--milestones-py', self._milestones_py, '--branch-py',
+        self._branch_py, '--main-star', self._main_star, '--dev-star',
+        self._dev_star
+    ]
+    cmd += args or []
+    return subprocess.run(cmd,
+                          env=env,
+                          text=True,
+                          stdout=subprocess.PIPE,
+                          stderr=subprocess.STDOUT)
+
+  def test_branch_day_invocation_fails(self):
+    result = self._execute_branch_day_py(
+        ['--milestone', 'XX', '--branch', 'YYYY'],
+        mock_details={
+            self._milestones_py: {
+                'stdout': 'FAKE FAILURE STDOUT',
+                'stderr': 'FAKE FAILURE STDERR',
+                'exit_code': 1,
+            }
+        })
+    self.assertNotEqual(result.returncode, 0)
+    expected_output = '\n'.join([
+        'Executing {} failed'.format([
+            self._milestones_py, 'activate', '--milestone', 'XX', '--branch',
+            'YYYY'
+        ]),
+        'FAKE FAILURE STDOUT',
+        'FAKE FAILURE STDERR',
+        '',
+    ])
+    self.assertEqual(result.stdout, expected_output)
+
+  def test_branch_day(self):
+    result = self._execute_branch_day_py(
+        ['--milestone', 'XX', '--branch', 'YYYY'])
+    self.assertEqual(result.returncode, 0,
+                     (f'subprocess failed\n***COMMAND***\n{result.args}\n'
+                      f'***OUTPUT***\n{result.stdout}\n'))
+    self.assertEqual(result.stdout, '')
+
+    with open(self._invocations_file) as f:
+      invocations = json.load(f)
+    expected_invocations = [
+        [
+            self._milestones_py, 'activate', '--milestone', 'XX', '--branch',
+            'YYYY'
+        ],
+        [self._main_star],
+        [self._dev_star],
+    ]
+    self.assertEqual(invocations, expected_invocations)
+
+  def test_branch_day_on_branch(self):
+    result = self._execute_branch_day_py(
+        ['--on-branch', '--milestone', 'XX', '--branch', 'YYYY'])
+    self.assertEqual(result.returncode, 0,
+                     (f'subprocess failed\n***COMMAND***\n{result.args}\n'
+                      f'***OUTPUT***\n{result.stdout}\n'))
+    self.assertEqual(result.stdout, '')
+
+    with open(self._invocations_file) as f:
+      invocations = json.load(f)
+    expected_invocations = [
+        [
+            self._branch_py, 'initialize', '--milestone', 'XX', '--branch',
+            'YYYY'
+        ],
+        [self._main_star],
+        [self._dev_star],
+    ]
+    self.assertEqual(invocations, expected_invocations)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/infra/config/scripts/tests/utils/mock.py b/infra/config/scripts/tests/utils/mock.py
new file mode 100755
index 0000000..67533bf3
--- /dev/null
+++ b/infra/config/scripts/tests/utils/mock.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env vpython
+# Copyright 2021 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.
+"""A binary to enable mocking of binaries.
+
+This enables writing tests to verify that a binary was invoked as well
+as controlling the output and exit code of the binary. Tests should
+copy or link to this binary so that invocations for different binaries
+can be distinguished.
+
+INVOCATIONS_FILE must be set to the location of the file to record
+invocations to. The format of the recorded invocations file will be a
+json list where each element is the sys.argv for the invocation. After
+running the code under test, the test can load the contents of the file
+and verify that the expected invocations occurred.
+
+Tests can control the output, error output and exit codes of the
+invocations via the environment variable MOCK_DETAILS. If set,
+MOCK_DETAILS must be a string containing a json-encoding mapping from
+the binary path used for the invocation to a mapping containing any or
+all of the following entries:
+* stdout -> string containing the program's stdout
+* stderr -> string containing the program's stderr
+* exit_code -> integer containing the program's exit code
+If both 'stdout' and 'stderr' are present in the mapping for a binary
+path, the stdout will be output first, no interleaving is supported.
+"""
+
+import json
+import os
+import sys
+
+invocations_file = os.environ['INVOCATIONS_FILE']
+
+if not os.path.exists(invocations_file):
+  invocations = []
+else:
+  with open(invocations_file) as f:
+    invocations = json.load(f)
+
+invocations.append(sys.argv)
+
+with open(invocations_file, 'w') as f:
+  json.dump(invocations, f)
+
+mock_details = json.loads(os.environ.get('MOCK_DETAILS', '{}'))
+mock_details = mock_details.get(sys.argv[0], {})
+
+mock_stdout = mock_details.get('stdout')
+if mock_stdout is not None:
+  print(mock_stdout)
+  sys.stdout.flush()
+
+mock_stderr = mock_details.get('stderr')
+if mock_stderr is not None:
+  print(mock_stderr, file=sys.stderr)
+
+mock_exit_code = mock_details.get('exit_code')
+if mock_exit_code is not None:
+  sys.exit(mock_exit_code)
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 9a9f78f..49ef2fc 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -3163,15 +3163,6 @@
 )
 
 ci.updater_builder(
-    name = "mac10.13-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.13",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
     name = "mac10.13-updater-tester-rel",
     console_view_entry = consoles.console_view_entry(
         category = "release|mac",
@@ -3190,6 +3181,15 @@
 )
 
 ci.updater_builder(
+    name = "mac10.15-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.15",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.updater_builder(
     name = "mac10.15-updater-tester-rel",
     console_view_entry = consoles.console_view_entry(
         category = "release|mac",
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index c109071..af01519 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -203,7 +203,7 @@
       By signing out, your bookmarks, history, passwords, and other Chromium data will no longer be synced to your Google Account.
       </message>
       <message name="IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT" desc="Text displayed in an alert when the user is signed out due to browser sign-in becoming disabled by policy. [iOS only]">
-        When you were signed out of Chromium, your bookmarks, history, passwords and other settings were cleared from this device. Your synced data is still saved to your account.
+        You can still see all your bookmarks, history, passwords and other settings on this device. If you make changes, they won't sync to your account.
       </message>
       <message name="IDS_IOS_FACE_ID_USAGE_DESCRIPTION" desc="Specifies the reason for using the device's Face ID capabilities.">
         Chromium uses Face ID to ensure authorized access to your passwords.
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
index ea97b449..1d58d9e 100644
--- a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
@@ -1 +1 @@
-b517c3c1d8a71738d33a08627b2fbd2426e59af8
\ No newline at end of file
+f90c18ced0e18d66622f7feccebf974041476b3c
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index 9b6562a..91edde4 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -203,7 +203,7 @@
       By signing out, your bookmarks, history, passwords, and other Chrome data will no longer be synced to your Google Account.
       </message>
       <message name="IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT" desc="Text displayed in an alert when the user is signed out due to browser sign-in becoming disabled by policy. [iOS only]">
-        When you were signed out of Chrome, your bookmarks, history, passwords and other settings were cleared from this device. Your synced Chrome data is still saved to your Google account.
+        You can still see all your bookmarks, history, passwords and other settings on this device. If you make changes, they won't sync to your Google Account.
       </message>
       <message name="IDS_IOS_FACE_ID_USAGE_DESCRIPTION" desc="Specifies the reason for using the device's Face ID capabilities.">
         Chrome uses Face ID to ensure authorized access to your passwords.
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
index ea97b449..1d58d9e 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT_SUBTEXT.png.sha1
@@ -1 +1 @@
-b517c3c1d8a71738d33a08627b2fbd2426e59af8
\ No newline at end of file
+f90c18ced0e18d66622f7feccebf974041476b3c
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index d420cd4..7585e89 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1731,7 +1731,7 @@
         This setting is enforced by your administrator.
       </message>
       <message name="IDS_IOS_ENTERPRISE_SIGNED_OUT" desc="Text displayed in an alert when the user is signed out due to browser sign-in becoming disabled by policy. [iOS only]">
-        Your organization signed you out.
+        Your organization signed you out
       </message>
       <message name="IDS_IOS_ENTERPRISE_SIGNED_OUT_CONTINUE" desc="The text in the 'Continue' button, displayed in an alert when the user is signed out due to browser sign-in becoming disabled by policy.">
         Continue
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT.png.sha1
index 9e0041cd..1d58d9e 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SIGNED_OUT.png.sha1
@@ -1 +1 @@
-4391757d30991da8417e34f2763497e2b8df0798
\ No newline at end of file
+f90c18ced0e18d66622f7feccebf974041476b3c
\ No newline at end of file
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 5275545..b4aaa7c 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -224,6 +224,16 @@
     {"Native UI", kDiscoverFeedInNtpEnableNativeUI,
      base::size(kDiscoverFeedInNtpEnableNativeUI), nullptr}};
 
+const FeatureEntry::FeatureParam kStartSurfaceReturnImmediately[] = {
+    {kReturnToStartSurfaceInactiveDurationInSeconds, "0"}};
+const FeatureEntry::FeatureParam kStartSurfaceReturnInOneHour[] = {
+    {kReturnToStartSurfaceInactiveDurationInSeconds, "3600"}};
+const FeatureEntry::FeatureVariation kStartSurfaceVariations[] = {
+    {"Return immediately", kStartSurfaceReturnImmediately,
+     base::size(kStartSurfaceReturnImmediately), nullptr},
+    {"Return in one hour", kStartSurfaceReturnInOneHour,
+     base::size(kStartSurfaceReturnInOneHour), nullptr}};
+
 const FeatureEntry::FeatureParam kWebViewNativeContextMenuWeb[] = {
     {web::features::kWebViewNativeContextMenuName,
      web::features::kWebViewNativeContextMenuParameterWeb}};
@@ -610,7 +620,9 @@
      FEATURE_VALUE_TYPE(shared_highlighting::kSharedHighlightingUseBlocklist)},
     {"start-surface", flag_descriptions::kStartSurfaceName,
      flag_descriptions::kStartSurfaceDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kStartSurface)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(kStartSurface,
+                                    kStartSurfaceVariations,
+                                    "StartSurface")},
     {"ios-crashpad", flag_descriptions::kCrashpadIOSName,
      flag_descriptions::kCrashpadIOSDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kCrashpadIOS)},
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service.h b/ios/chrome/browser/safe_browsing/chrome_password_protection_service.h
index 8309182..aed8eed 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -5,6 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_CHROME_PASSWORD_PROTECTION_SERVICE_H_
 #define IOS_CHROME_BROWSER_SAFE_BROWSING_CHROME_PASSWORD_PROTECTION_SERVICE_H_
 
+#include <string>
 #include <vector>
 
 #include "base/strings/string16.h"
@@ -20,38 +21,45 @@
 
 namespace password_manager {
 class PasswordStore;
-}
+}  // namespace password_manager
+
+namespace safe_browsing {
+class PasswordProtectionRequest;
+}  // namespace safe_browsing
 
 namespace web {
 class WebState;
-}
+}  // namespace web
 
-namespace safe_browsing {
-
-class ChromePasswordProtectionService : public PasswordProtectionService,
-                                        public KeyedService {
+class ChromePasswordProtectionService
+    : public safe_browsing::PasswordProtectionService,
+      public KeyedService {
  public:
   explicit ChromePasswordProtectionService(ChromeBrowserState* browser_state);
   ~ChromePasswordProtectionService() override;
 
-  void ShowModalWarning(PasswordProtectionRequest* request,
-                        LoginReputationClientResponse::VerdictType verdict_type,
-                        const std::string& verdict_token,
-                        ReusedPasswordAccountType password_type) override;
+  void ShowModalWarning(
+      safe_browsing::PasswordProtectionRequest* request,
+      safe_browsing::LoginReputationClientResponse::VerdictType verdict_type,
+      const std::string& verdict_token,
+      safe_browsing::ReusedPasswordAccountType password_type) override;
 
-  void MaybeReportPasswordReuseDetected(PasswordProtectionRequest* request,
-                                        const std::string& username,
-                                        PasswordType password_type,
-                                        bool is_phishing_url) override;
+  void MaybeReportPasswordReuseDetected(
+      safe_browsing::PasswordProtectionRequest* request,
+      const std::string& username,
+      safe_browsing::PasswordType password_type,
+      bool is_phishing_url) override;
 
   void ReportPasswordChanged() override;
 
-  void FillReferrerChain(const GURL& event_url,
-                         SessionID event_tab_id,  // SessionID::InvalidValue()
-                                                  // if tab not available.
-                         LoginReputationClientRequest::Frame* frame) override;
+  void FillReferrerChain(
+      const GURL& event_url,
+      SessionID event_tab_id,  // SessionID::InvalidValue()
+                               // if tab not available.
+      safe_browsing::LoginReputationClientRequest::Frame* frame) override;
 
-  void SanitizeReferrerChain(ReferrerChain* referrer_chain) override;
+  void SanitizeReferrerChain(
+      safe_browsing::ReferrerChain* referrer_chain) override;
 
   void PersistPhishedSavedPasswordCredential(
       const std::vector<password_manager::MatchingReusedCredential>&
@@ -61,23 +69,24 @@
       const std::vector<password_manager::MatchingReusedCredential>&
           matching_reused_credentials) override;
 
-  RequestOutcome GetPingNotSentReason(
-      LoginReputationClientRequest::TriggerType trigger_type,
+  safe_browsing::RequestOutcome GetPingNotSentReason(
+      safe_browsing::LoginReputationClientRequest::TriggerType trigger_type,
       const GURL& url,
-      ReusedPasswordAccountType password_type) override;
+      safe_browsing::ReusedPasswordAccountType password_type) override;
 
   void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
       bool all_history,
       const history::URLRows& deleted_rows) override;
 
   bool UserClickedThroughSBInterstitial(
-      PasswordProtectionRequest* request) override;
+      safe_browsing::PasswordProtectionRequest* request) override;
 
-  PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
-      ReusedPasswordAccountType password_type) const override;
+  safe_browsing::PasswordProtectionTrigger
+  GetPasswordProtectionWarningTriggerPref(
+      safe_browsing::ReusedPasswordAccountType password_type) const override;
 
-  LoginReputationClientRequest::UrlDisplayExperiment GetUrlDisplayExperiment()
-      const override;
+  safe_browsing::LoginReputationClientRequest::UrlDisplayExperiment
+  GetUrlDisplayExperiment() const override;
 
   const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
       const override;
@@ -87,20 +96,24 @@
   AccountInfo GetSignedInNonSyncAccount(
       const std::string& username) const override;
 
-  LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
-  GetSyncAccountType() const override;
+  safe_browsing::LoginReputationClientRequest::PasswordReuseEvent::
+      SyncAccountType
+      GetSyncAccountType() const override;
 
-  bool CanShowInterstitial(ReusedPasswordAccountType password_type,
-                           const GURL& main_frame_url) override;
+  bool CanShowInterstitial(
+      safe_browsing::ReusedPasswordAccountType password_type,
+      const GURL& main_frame_url) override;
 
   bool IsURLAllowlistedForPasswordEntry(const GURL& url) const override;
 
-  bool IsInPasswordAlertMode(ReusedPasswordAccountType password_type) override;
+  bool IsInPasswordAlertMode(
+      safe_browsing::ReusedPasswordAccountType password_type) override;
 
   bool CanSendSamplePing() override;
 
-  bool IsPingingEnabled(LoginReputationClientRequest::TriggerType trigger_type,
-                        ReusedPasswordAccountType password_type) override;
+  bool IsPingingEnabled(
+      safe_browsing::LoginReputationClientRequest::TriggerType trigger_type,
+      safe_browsing::ReusedPasswordAccountType password_type) override;
 
   bool IsIncognito() override;
 
@@ -125,9 +138,9 @@
   // PasswordProtectionService override.
   void MaybeLogPasswordReuseLookupEvent(
       web::WebState* web_state,
-      RequestOutcome outcome,
-      PasswordType password_type,
-      const LoginReputationClientResponse* response) override;
+      safe_browsing::RequestOutcome outcome,
+      safe_browsing::PasswordType password_type,
+      const safe_browsing::LoginReputationClientResponse* response) override;
 
   // Records a Chrome Sync event that sync password reuse was detected.
   void MaybeLogPasswordReuseDetectedEvent(web::WebState* web_state);
@@ -144,7 +157,7 @@
   // placeholders that are passed into the resource string. It is only set for
   // saved passwords.
   base::string16 GetWarningDetailText(
-      ReusedPasswordAccountType password_type,
+      safe_browsing::ReusedPasswordAccountType password_type,
       std::vector<size_t>* placeholder_offsets) const;
 
   // Gets the warning text for saved password reuse warnings.
@@ -165,6 +178,10 @@
   std::vector<base::string16> GetPlaceholdersForSavedPasswordWarningText()
       const;
 
+ protected:
+  FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceTest,
+                           VerifySendsPingForAboutBlank);
+
  private:
   password_manager::PasswordStore* GetStoreForReusedCredential(
       const password_manager::MatchingReusedCredential& reused_credential);
@@ -178,7 +195,7 @@
   password_manager::PasswordStore* GetAccountPasswordStore() const;
 
   // Gets prefs associated with |browser_state_|.
-  PrefService* GetPrefs();
+  PrefService* GetPrefs() const;
 
   // Returns whether |browser_state_| has safe browsing service enabled.
   bool IsSafeBrowsingEnabled();
@@ -186,6 +203,4 @@
   ChromeBrowserState* browser_state_;
 };
 
-}  // namespace safe_browsing
-
 #endif  // IOS_CHROME_BROWSER_SAFE_BROWSING_CHROME_PASSWORD_PROTECTION_SERVICE_H_
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service.mm b/ios/chrome/browser/safe_browsing/chrome_password_protection_service.mm
index 810bfce75..b40af94 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service.mm
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service.mm
@@ -6,10 +6,12 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/features.h"
 #include "components/strings/grit/components_strings.h"
@@ -23,23 +25,29 @@
 #include "ios/web/public/thread/web_thread.h"
 #import "ios/web/public/web_state.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-using sync_pb::GaiaPasswordReuse;
+using password_manager::metrics_util::PasswordType;
+using safe_browsing::LoginReputationClientRequest;
+using safe_browsing::LoginReputationClientResponse;
+using safe_browsing::PasswordProtectionTrigger;
+using safe_browsing::RequestOutcome;
+using safe_browsing::ReusedPasswordAccountType;
 using sync_pb::UserEventSpecifics;
-using InteractionResult =
-    GaiaPasswordReuse::PasswordReuseDialogInteraction::InteractionResult;
+using safe_browsing::ReferrerChain;
+
+using InteractionResult = sync_pb::GaiaPasswordReuse::
+    PasswordReuseDialogInteraction::InteractionResult;
 using PasswordReuseDialogInteraction =
-    GaiaPasswordReuse::PasswordReuseDialogInteraction;
+    sync_pb::GaiaPasswordReuse::PasswordReuseDialogInteraction;
 using PasswordReuseEvent =
     safe_browsing::LoginReputationClientRequest::PasswordReuseEvent;
 using SafeBrowsingStatus =
-    GaiaPasswordReuse::PasswordReuseDetected::SafeBrowsingStatus;
-
-namespace safe_browsing {
+    sync_pb::GaiaPasswordReuse::PasswordReuseDetected::SafeBrowsingStatus;
 
 namespace {
 
@@ -86,13 +94,13 @@
 
 ChromePasswordProtectionService::ChromePasswordProtectionService(
     ChromeBrowserState* browser_state)
-    : PasswordProtectionService(nullptr, nullptr, nullptr),
+    : safe_browsing::PasswordProtectionService(nullptr, nullptr, nullptr),
       browser_state_(browser_state) {}
 
 ChromePasswordProtectionService::~ChromePasswordProtectionService() = default;
 
 void ChromePasswordProtectionService::ShowModalWarning(
-    PasswordProtectionRequest* request,
+    safe_browsing::PasswordProtectionRequest* request,
     LoginReputationClientResponse::VerdictType verdict_type,
     const std::string& verdict_token,
     ReusedPasswordAccountType password_type) {
@@ -100,7 +108,7 @@
 }
 
 void ChromePasswordProtectionService::MaybeReportPasswordReuseDetected(
-    PasswordProtectionRequest* request,
+    safe_browsing::PasswordProtectionRequest* request,
     const std::string& username,
     PasswordType password_type,
     bool is_phishing_url) {
@@ -168,7 +176,36 @@
     LoginReputationClientRequest::TriggerType trigger_type,
     const GURL& url,
     ReusedPasswordAccountType password_type) {
-  // TODO(crbug.com/1147967): Complete PhishGuard iOS implementation.
+  DCHECK(!CanSendPing(trigger_type, url, password_type));
+  if (IsInExcludedCountry()) {
+    return RequestOutcome::EXCLUDED_COUNTRY;
+  }
+  if (!IsSafeBrowsingEnabled()) {
+    return RequestOutcome::SAFE_BROWSING_DISABLED;
+  }
+  if (IsIncognito()) {
+    return RequestOutcome::DISABLED_DUE_TO_INCOGNITO;
+  }
+  if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+      password_type.account_type() !=
+          ReusedPasswordAccountType::SAVED_PASSWORD &&
+      GetPasswordProtectionWarningTriggerPref(password_type) ==
+          safe_browsing::PASSWORD_PROTECTION_OFF) {
+    return RequestOutcome::TURNED_OFF_BY_ADMIN;
+  }
+  PrefService* prefs = browser_state_->GetPrefs();
+  if (safe_browsing::IsURLAllowlistedByPolicy(url, *prefs)) {
+    return RequestOutcome::MATCHED_ENTERPRISE_ALLOWLIST;
+  }
+  if (safe_browsing::MatchesPasswordProtectionChangePasswordURL(url, *prefs)) {
+    return RequestOutcome::MATCHED_ENTERPRISE_CHANGE_PASSWORD_URL;
+  }
+  if (safe_browsing::MatchesPasswordProtectionLoginURL(url, *prefs)) {
+    return RequestOutcome::MATCHED_ENTERPRISE_LOGIN_URL;
+  }
+  if (IsInPasswordAlertMode(password_type)) {
+    return RequestOutcome::PASSWORD_ALERT_MODE;
+  }
   return RequestOutcome::DISABLED_DUE_TO_USER_POPULATION;
 }
 
@@ -180,7 +217,7 @@
 }
 
 bool ChromePasswordProtectionService::UserClickedThroughSBInterstitial(
-    PasswordProtectionRequest* request) {
+    safe_browsing::PasswordProtectionRequest* request) {
   // TODO(crbug.com/1147967): Complete PhishGuard iOS implementation.
   return false;
 }
@@ -188,8 +225,18 @@
 PasswordProtectionTrigger
 ChromePasswordProtectionService::GetPasswordProtectionWarningTriggerPref(
     ReusedPasswordAccountType password_type) const {
-  // TODO(crbug.com/1147967): Complete PhishGuard iOS implementation.
-  return PHISHING_REUSE;
+  if (password_type.account_type() ==
+          ReusedPasswordAccountType::SAVED_PASSWORD &&
+      base::FeatureList::IsEnabled(
+          safe_browsing::kPasswordProtectionForSavedPasswords))
+    return safe_browsing::PHISHING_REUSE;
+
+  bool is_policy_managed =
+      GetPrefs()->HasPrefPath(prefs::kPasswordProtectionWarningTrigger);
+  PasswordProtectionTrigger trigger_level =
+      static_cast<PasswordProtectionTrigger>(
+          GetPrefs()->GetInteger(prefs::kPasswordProtectionWarningTrigger));
+  return is_policy_managed ? trigger_level : safe_browsing::PHISHING_REUSE;
 }
 
 LoginReputationClientRequest::UrlDisplayExperiment
@@ -235,15 +282,16 @@
     return false;
 
   PrefService* prefs = browser_state_->GetPrefs();
-  return IsURLAllowlistedByPolicy(url, *prefs) ||
-         MatchesPasswordProtectionChangePasswordURL(url, *prefs) ||
-         MatchesPasswordProtectionLoginURL(url, *prefs);
+  return safe_browsing::IsURLAllowlistedByPolicy(url, *prefs) ||
+         safe_browsing::MatchesPasswordProtectionChangePasswordURL(url,
+                                                                   *prefs) ||
+         safe_browsing::MatchesPasswordProtectionLoginURL(url, *prefs);
 }
 
 bool ChromePasswordProtectionService::IsInPasswordAlertMode(
     ReusedPasswordAccountType password_type) {
-  // TODO(crbug.com/1147967): Complete PhishGuard iOS implementation.
-  return false;
+  return GetPasswordProtectionWarningTriggerPref(password_type) ==
+         safe_browsing::PASSWORD_REUSE;
 }
 
 bool ChromePasswordProtectionService::CanSendSamplePing() {
@@ -501,7 +549,7 @@
   return nullptr;
 }
 
-PrefService* ChromePasswordProtectionService::GetPrefs() {
+PrefService* ChromePasswordProtectionService::GetPrefs() const {
   return browser_state_->GetPrefs();
 }
 
@@ -509,4 +557,3 @@
   return ::safe_browsing::IsSafeBrowsingEnabled(*GetPrefs());
 }
 
-}  // namespace safe_browsing
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.h b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.h
index 3f6a752..0d094aa 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.h
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.h
@@ -10,11 +10,8 @@
 #include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-class KeyedService;
-
-namespace safe_browsing {
 class ChromePasswordProtectionService;
-}
+class KeyedService;
 
 namespace web {
 class BrowserState;
@@ -27,7 +24,7 @@
  public:
   // Returns the instance of ChromePasswordProtectionService associated with
   // this browser state, creating one if none exists.
-  static safe_browsing::ChromePasswordProtectionService* GetForBrowserState(
+  static ChromePasswordProtectionService* GetForBrowserState(
       web::BrowserState* browser_state);
 
   // Returns the singleton instance of ChromePasswordProtectionServiceFactory.
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.mm b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.mm
index c348c3d..7584a03 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.mm
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_factory.mm
@@ -16,10 +16,10 @@
 #endif
 
 // static
-safe_browsing::ChromePasswordProtectionService*
+ChromePasswordProtectionService*
 ChromePasswordProtectionServiceFactory::GetForBrowserState(
     web::BrowserState* browser_state) {
-  return static_cast<safe_browsing::ChromePasswordProtectionService*>(
+  return static_cast<ChromePasswordProtectionService*>(
       GetInstance()->GetServiceForBrowserState(browser_state, /*create=*/true));
 }
 
@@ -43,7 +43,7 @@
     web::BrowserState* browser_state) const {
   ChromeBrowserState* chrome_browser_state =
       ChromeBrowserState::FromBrowserState(browser_state);
-  return std::make_unique<safe_browsing::ChromePasswordProtectionService>(
+  return std::make_unique<ChromePasswordProtectionService>(
       chrome_browser_state);
 }
 
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
index 8dbf91d..0a49c49 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
@@ -44,13 +44,14 @@
 using ::testing::_;
 using password_manager::metrics_util::PasswordType;
 using password_manager::MockPasswordStore;
+using safe_browsing::LoginReputationClientRequest;
+using safe_browsing::PasswordProtectionTrigger;
+using safe_browsing::RequestOutcome;
+using safe_browsing::ReusedPasswordAccountType;
 using sync_pb::GaiaPasswordReuse;
-using sync_pb::UserEventSpecifics;
 using PasswordReuseDialogInteraction =
-    GaiaPasswordReuse::PasswordReuseDialogInteraction;
-using PasswordReuseLookup = GaiaPasswordReuse::PasswordReuseLookup;
-
-namespace safe_browsing {
+    sync_pb::GaiaPasswordReuse::PasswordReuseDialogInteraction;
+using PasswordReuseLookup = sync_pb::GaiaPasswordReuse::PasswordReuseLookup;
 
 namespace {
 
@@ -221,7 +222,7 @@
   service_->SetIsNoHostedDomainFound(true);
   EXPECT_FALSE(service_->IsPingingEnabled(trigger_type, reused_password_type));
   chrome_browser_state_->GetPrefs()->SetInteger(
-      prefs::kPasswordProtectionWarningTrigger, PASSWORD_REUSE);
+      prefs::kPasswordProtectionWarningTrigger, safe_browsing::PASSWORD_REUSE);
   EXPECT_FALSE(service_->IsPingingEnabled(trigger_type, reused_password_type));
 }
 
@@ -284,12 +285,13 @@
   EXPECT_FALSE(service_->IsPingingEnabled(trigger_type, reused_password_type));
 
   chrome_browser_state_->GetPrefs()->SetInteger(
-      prefs::kPasswordProtectionWarningTrigger, PASSWORD_PROTECTION_OFF);
+      prefs::kPasswordProtectionWarningTrigger,
+      safe_browsing::PASSWORD_PROTECTION_OFF);
   service_->SetIsIncognito(false);
   EXPECT_FALSE(service_->IsPingingEnabled(trigger_type, reused_password_type));
 
   chrome_browser_state_->GetPrefs()->SetInteger(
-      prefs::kPasswordProtectionWarningTrigger, PASSWORD_REUSE);
+      prefs::kPasswordProtectionWarningTrigger, safe_browsing::PASSWORD_REUSE);
   EXPECT_FALSE(service_->IsPingingEnabled(trigger_type, reused_password_type));
 }
 
@@ -502,4 +504,82 @@
             service_->GetPlaceholdersForSavedPasswordWarningText());
 }
 
-}  // namespace safe_browsing
+TEST_F(ChromePasswordProtectionServiceTest, VerifySendsPingForAboutBlank) {
+  ReusedPasswordAccountType reused_password_type;
+  reused_password_type.set_account_type(
+      ReusedPasswordAccountType::SAVED_PASSWORD);
+  service_->SetIsIncognito(false);
+  EXPECT_TRUE(
+      service_->CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                            GURL("about:blank"), reused_password_type));
+}
+
+TEST_F(ChromePasswordProtectionServiceTest, VerifyGetPingNotSentReason) {
+  {
+    // SBER disabled.
+    ReusedPasswordAccountType reused_password_type;
+    service_->SetIsIncognito(false);
+    EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_USER_POPULATION,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                  GURL("about:blank"), reused_password_type));
+    reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+    EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_USER_POPULATION,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                  GURL("about:blank"), reused_password_type));
+  }
+  {
+    // In Incognito.
+    ReusedPasswordAccountType reused_password_type;
+    service_->SetIsIncognito(true);
+    EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_INCOGNITO,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                  GURL("about:blank"), reused_password_type));
+  }
+  {
+    // Turned off by admin.
+    ReusedPasswordAccountType reused_password_type;
+    service_->SetIsIncognito(false);
+    reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+    chrome_browser_state_->GetPrefs()->SetInteger(
+        prefs::kPasswordProtectionWarningTrigger,
+        safe_browsing::PASSWORD_PROTECTION_OFF);
+    EXPECT_EQ(RequestOutcome::TURNED_OFF_BY_ADMIN,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                  GURL("about:blank"), reused_password_type));
+  }
+  {
+    // Allowlisted by policy.
+    ReusedPasswordAccountType reused_password_type;
+    service_->SetIsIncognito(false);
+    reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+    chrome_browser_state_->GetPrefs()->SetInteger(
+        prefs::kPasswordProtectionWarningTrigger,
+        safe_browsing::PHISHING_REUSE);
+    base::ListValue allowlist;
+    allowlist.AppendString("mydomain.com");
+    allowlist.AppendString("mydomain.net");
+    chrome_browser_state_->GetPrefs()->Set(prefs::kSafeBrowsingAllowlistDomains,
+                                           allowlist);
+    EXPECT_EQ(RequestOutcome::MATCHED_ENTERPRISE_ALLOWLIST,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                  GURL("https://www.mydomain.com"), reused_password_type));
+  }
+  {
+    // Password alert mode.
+    ReusedPasswordAccountType reused_password_type;
+    service_->SetIsIncognito(false);
+    reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+    chrome_browser_state_->GetPrefs()->SetInteger(
+        prefs::kPasswordProtectionWarningTrigger,
+        safe_browsing::PASSWORD_REUSE);
+    EXPECT_EQ(RequestOutcome::PASSWORD_ALERT_MODE,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                  GURL("about:blank"), reused_password_type));
+  }
+}
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 02d1bfa..08d8515f 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -128,6 +128,7 @@
     "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/sync",
+    "//ios/chrome/browser/ui/start_surface",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid",
     "//ios/chrome/browser/ui/thumb_strip:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index ecefebf5..c15e3f95 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -89,6 +89,8 @@
 #import "ios/chrome/browser/ui/main/ui_blocker_scene_agent.h"
 #import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_scene_agent.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_util.h"
 #include "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.h"
 #include "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/thumb_strip/thumb_strip_feature.h"
@@ -283,6 +285,7 @@
         addAgent:[[IncognitoReauthSceneAgent alloc]
                      initWithReauthModule:[[ReauthenticationModule alloc]
                                               init]]];
+    [_sceneState addAgent:[[StartSurfaceSceneAgent alloc] init]];
   }
   return self;
 }
@@ -389,6 +392,8 @@
       [applicationHandler openURLInNewTab:command];
       [self finishActivatingBrowserDismissingTabSwitcher:YES];
     }
+
+    [self handleShowStartSurfaceIfNecessary];
   }
 
   [self recordWindowCreationForSceneState:sceneState];
@@ -501,6 +506,41 @@
   }
 }
 
+// TODO(crbug.com/1173160): Split and move to the StartSurfaceSceneAgent after
+// refactoring the scene states.
+- (void)handleShowStartSurfaceIfNecessary {
+  // Keep showing the last active NTP tab no matter whether the Start Surface is
+  // enabled or not by design.
+  web::WebState* currentWebState =
+      self.currentInterface.browser->GetWebStateList()->GetActiveWebState();
+  if (IsURLNtp(currentWebState->GetVisibleURL())) {
+    return;
+  }
+
+  if (!ShouldShowStartSurfaceForSceneState(self.sceneState)) {
+    return;
+  }
+  self.sceneState.modifytVisibleNTPForStartSurface = YES;
+
+  // Activate the existing NTP tab for the Start surface.
+  WebStateList* webStateList = self.currentInterface.browser->GetWebStateList();
+  for (int i = 0; i < webStateList->count(); i++) {
+    if (IsURLNtp(webStateList->GetWebStateAt(i)->GetVisibleURL())) {
+      webStateList->ActivateWebStateAt(i);
+      return;
+    }
+  }
+
+  // Open a new NTP tab if there is no existing NTP tab for the Start Surface.
+  OpenNewTabCommand* command =
+      [OpenNewTabCommand commandWithIncognito:self.currentInterface.incognito];
+  command.userInitiated = NO;
+  Browser* browser = self.currentInterface.browser;
+  id<ApplicationCommands> applicationHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), ApplicationCommands);
+  [applicationHandler openURLInNewTab:command];
+}
+
 - (void)recordWindowCreationForSceneState:(SceneState*)sceneState {
   // Don't record window creation for single-window environments
   if (!base::ios::IsMultipleScenesSupported())
@@ -1246,6 +1286,8 @@
     if (!service->IsAuthenticated()) {
       return;
     }
+    UMA_HISTOGRAM_BOOLEAN("Enterprise.BrowserSigninIOS.SignedOutByPolicy",
+                          true);
     // Sign the user out, but keep synced data (bookmarks, passwords, etc)
     // locally to be consistent with the policy's behavior on other platforms.
     service->SignOut(
@@ -1266,6 +1308,8 @@
 
   if (self.signinCoordinator) {
     [self interruptSigninCoordinatorAnimated:YES completion:signOut];
+    UMA_HISTOGRAM_BOOLEAN(
+        "Enterprise.BrowserSigninIOS.SignInInterruptedByPolicy", true);
   } else {
     signOut();
   }
diff --git a/ios/chrome/browser/ui/main/scene_state.h b/ios/chrome/browser/ui/main/scene_state.h
index 6349cb7b3..feab947 100644
--- a/ios/chrome/browser/ui/main/scene_state.h
+++ b/ios/chrome/browser/ui/main/scene_state.h
@@ -111,6 +111,13 @@
 // YES if the QR scanner is visible.
 @property(nonatomic, assign) BOOL QRScannerVisible;
 
+// YES if the visible NTP should be modified for the Start Surface.
+//
+// This flag is set by SceneController to YES when the Start Surface should be
+// shown. It is checked by the NewTabPageCoordinator to modify the NTP
+// accordingly, and then reset it to NO.
+@property(nonatomic, assign) BOOL modifytVisibleNTPForStartSurface;
+
 // Adds an observer to this scene state. The observers will be notified about
 // scene state changes per SceneStateObserver protocol.
 - (void)addObserver:(id<SceneStateObserver>)observer;
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
index ea540c6a..c978951a 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_egtest.mm
@@ -117,7 +117,13 @@
 
 // Tests the different fixed elements (labels, buttons) are present on the
 // screen.
-- (void)testElementsPresent {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testElementsPresent DISABLED_testElementsPresent
+#else
+#define MAYBE_testElementsPresent testElementsPresent
+#endif
+- (void)MAYBE_testElementsPresent {
   [[EarlGrey selectElementWithMatcher:NameOnCardField()]
       assertWithMatcher:grey_sufficientlyVisible()];
   [[EarlGrey selectElementWithMatcher:CardNumberField()]
@@ -137,7 +143,14 @@
 #pragma mark - Test top toolbar buttons
 
 // Tests that the 'Add' button in the top toolbar is disabled by default.
-- (void)testAddButtonDisabledOnDefault {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonDisabledOnDefault \
+  DISABLED_testAddButtonDisabledOnDefault
+#else
+#define MAYBE_testAddButtonDisabledOnDefault testAddButtonDisabledOnDefault
+#endif
+- (void)MAYBE_testAddButtonDisabledOnDefault {
   [[EarlGrey selectElementWithMatcher:chrome_test_util::AddCreditCardButton()]
       assertWithMatcher:grey_allOf(grey_sufficientlyVisible(),
                                    grey_not(grey_enabled()), nil)];
@@ -145,7 +158,14 @@
 
 // Tests that the 'Cancel' button dismisses the screen.
 // TODO(crbug.com/1149306): test flaky on iPads.
-- (void)testCancelButtonDismissesScreen {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testCancelButtonDismissesScreen \
+  DISABLED_testCancelButtonDismissesScreen
+#else
+#define MAYBE_testCancelButtonDismissesScreen testCancelButtonDismissesScreen
+#endif
+- (void)MAYBE_testCancelButtonDismissesScreen {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_DISABLED(@"Fails on iPad.");
   }
@@ -163,7 +183,15 @@
 
 // Tests when a user tries to add an invalid card number, the "Add" button is
 // not enabled.
-- (void)testAddButtonDisabledOnInvalidNumber {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonDisabledOnInvalidNumber \
+  DISABLED_testAddButtonDisabledOnInvalidNumber
+#else
+#define MAYBE_testAddButtonDisabledOnInvalidNumber \
+  testAddButtonDisabledOnInvalidNumber
+#endif
+- (void)MAYBE_testAddButtonDisabledOnInvalidNumber {
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_replaceText(@"1234")];
 
@@ -174,7 +202,15 @@
 
 // Tests when a user tries to add an invalid card number, the "Add" button is
 // not enabled.
-- (void)testAddButtonDisabledOnInvalidExpiryDate {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonDisabledOnInvalidExpiryDate \
+  DISABLED_testAddButtonDisabledOnInvalidExpiryDate
+#else
+#define MAYBE_testAddButtonDisabledOnInvalidExpiryDate \
+  testAddButtonDisabledOnInvalidExpiryDate
+#endif
+- (void)MAYBE_testAddButtonDisabledOnInvalidExpiryDate {
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_replaceText(@"4111111111111111")];
   [[EarlGrey selectElementWithMatcher:MonthOfExpiryTextField()]
@@ -189,7 +225,15 @@
 
 // Tests when a user tries to add an invalid card nickname, the "Add" button is
 // not enabled.
-- (void)testAddButtonDisabledOnInvalidNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonDisabledOnInvalidNickname \
+  DISABLED_testAddButtonDisabledOnInvalidNickname
+#else
+#define MAYBE_testAddButtonDisabledOnInvalidNickname \
+  testAddButtonDisabledOnInvalidNickname
+#endif
+- (void)MAYBE_testAddButtonDisabledOnInvalidNickname {
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_replaceText(@"4111111111111111")];
   [[EarlGrey selectElementWithMatcher:MonthOfExpiryTextField()]
@@ -206,7 +250,15 @@
 
 // Tests when a user tries to add an empty card nickname, the "Add" button is
 // enabled.
-- (void)testAddButtonEnabledOnEmptyNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonEnabledOnEmptyNickname \
+  DISABLED_testAddButtonEnabledOnEmptyNickname
+#else
+#define MAYBE_testAddButtonEnabledOnEmptyNickname \
+  testAddButtonEnabledOnEmptyNickname
+#endif
+- (void)MAYBE_testAddButtonEnabledOnEmptyNickname {
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_replaceText(@"4111111111111111")];
   [[EarlGrey selectElementWithMatcher:MonthOfExpiryTextField()]
@@ -223,7 +275,13 @@
 // and the new card number appears on the Autofill Credit Card 'Payment Methods'
 // screen.
 // TODO(crbug.com/1149306): test flaky on iPads.
-- (void)testAddButtonOnValidNumber {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonOnValidNumber DISABLED_testAddButtonOnValidNumber
+#else
+#define MAYBE_testAddButtonOnValidNumber testAddButtonOnValidNumber
+#endif
+- (void)MAYBE_testAddButtonOnValidNumber {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_DISABLED(@"Fails on iPad.");
   }
@@ -253,7 +311,13 @@
 // Tests when a user add a card with a nickname, the screen is dismissed
 // and the new card number appears on the Autofill Credit Card 'Payment Methods'
 // screen with the nickname.
-- (void)testAddButtonOnValidNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonOnValidNickname DISABLED_testAddButtonOnValidNickname
+#else
+#define MAYBE_testAddButtonOnValidNickname testAddButtonOnValidNickname
+#endif
+- (void)MAYBE_testAddButtonOnValidNickname {
   [AutofillAppInterface clearCreditCardStore];
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_replaceText(@"4111111111111111")];
@@ -278,7 +342,15 @@
 
 // Tests that an error icon is displayed when a field has invalid text. The icon
 // is displayed if the field is not currently being editted.
-- (void)testInvalidInputDisplaysInlineError {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testInvalidInputDisplaysInlineError \
+  DISABLED_testInvalidInputDisplaysInlineError
+#else
+#define MAYBE_testInvalidInputDisplaysInlineError \
+  testInvalidInputDisplaysInlineError
+#endif
+- (void)MAYBE_testInvalidInputDisplaysInlineError {
   [[EarlGrey selectElementWithMatcher:CardNumberIconView(kEditIconIdentifier)]
       assertWithMatcher:grey_sufficientlyVisible()];
 
@@ -309,7 +381,15 @@
 
 // Tests that add button is disabled until typing a single character makes all
 // the fields valid.
-- (void)testAddButtonDisabledTillValidForm {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAddButtonDisabledTillValidForm \
+  DISABLED_testAddButtonDisabledTillValidForm
+#else
+#define MAYBE_testAddButtonDisabledTillValidForm \
+  testAddButtonDisabledTillValidForm
+#endif
+- (void)MAYBE_testAddButtonDisabledTillValidForm {
   [[EarlGrey selectElementWithMatcher:CardNumberTextField()]
       performAction:grey_typeText(@"4111111111111111")];
   [[EarlGrey selectElementWithMatcher:MonthOfExpiryTextField()]
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
index ddd26e4..27f61f3 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
@@ -124,7 +124,13 @@
 }
 
 // Test that the page for viewing Autofill credit card details is as expected.
-- (void)testCreditCardViewPage {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testCreditCardViewPage DISABLED_testCreditCardViewPage
+#else
+#define MAYBE_testCreditCardViewPage testCreditCardViewPage
+#endif
+- (void)MAYBE_testCreditCardViewPage {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openEditCreditCard:[self creditCardLabel:lastDigits]];
 
@@ -147,7 +153,15 @@
 }
 
 // Test that the page for viewing Autofill credit card details is accessible.
-- (void)testAccessibilityOnCreditCardViewPage {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAccessibilityOnCreditCardViewPage \
+  DISABLED_testAccessibilityOnCreditCardViewPage
+#else
+#define MAYBE_testAccessibilityOnCreditCardViewPage \
+  testAccessibilityOnCreditCardViewPage
+#endif
+- (void)MAYBE_testAccessibilityOnCreditCardViewPage {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openEditCreditCard:[self creditCardLabel:lastDigits]];
 
@@ -161,7 +175,15 @@
 }
 
 // Test that the page for editing Autofill credit card details is accessible.
-- (void)testAccessibilityOnCreditCardEditPage {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAccessibilityOnCreditCardEditPage \
+  DISABLED_testAccessibilityOnCreditCardEditPage
+#else
+#define MAYBE_testAccessibilityOnCreditCardEditPage \
+  testAccessibilityOnCreditCardEditPage
+#endif
+- (void)MAYBE_testAccessibilityOnCreditCardEditPage {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openEditCreditCard:[self creditCardLabel:lastDigits]];
 
@@ -179,7 +201,13 @@
 
 // Checks that the Autofill credit cards list view is in edit mode and the
 // Autofill credit cards switch is disabled.
-- (void)testListViewEditMode {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testListViewEditMode DISABLED_testListViewEditMode
+#else
+#define MAYBE_testListViewEditMode testListViewEditMode
+#endif
+- (void)MAYBE_testListViewEditMode {
   [AutofillAppInterface saveLocalCreditCard];
   [self openCreditCardsSettings];
 
@@ -197,7 +225,13 @@
 
 // Checks that the Autofill credit card switch can be toggled on/off and the
 // list of Autofill credit cards is not affected by it.
-- (void)testToggleCreditCardSwitch {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testToggleCreditCardSwitch DISABLED_testToggleCreditCardSwitch
+#else
+#define MAYBE_testToggleCreditCardSwitch testToggleCreditCardSwitch
+#endif
+- (void)MAYBE_testToggleCreditCardSwitch {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openCreditCardsSettings];
 
@@ -227,7 +261,15 @@
 }
 
 // Checks that the toolbar always appears in edit mode.
-- (void)testToolbarInEditModeAddPaymentMethodFeatureEnabled {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testToolbarInEditModeAddPaymentMethodFeatureEnabled \
+  DISABLED_testToolbarInEditModeAddPaymentMethodFeatureEnabled
+#else
+#define MAYBE_testToolbarInEditModeAddPaymentMethodFeatureEnabled \
+  testToolbarInEditModeAddPaymentMethodFeatureEnabled
+#endif
+- (void)MAYBE_testToolbarInEditModeAddPaymentMethodFeatureEnabled {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openCreditCardListInEditMode];
 
@@ -247,7 +289,15 @@
 
 // Checks the 'Add Payment Method' button is always visible and directs a user
 // to the Add Payent method view.
-- (void)testToolbarAddPaymentMethodButtonFeatureEnabled {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testToolbarAddPaymentMethodButtonFeatureEnabled \
+  DISABLED_testToolbarAddPaymentMethodButtonFeatureEnabled
+#else
+#define MAYBE_testToolbarAddPaymentMethodButtonFeatureEnabled \
+  testToolbarAddPaymentMethodButtonFeatureEnabled
+#endif
+- (void)MAYBE_testToolbarAddPaymentMethodButtonFeatureEnabled {
   [AutofillAppInterface saveLocalCreditCard];
   [self openCreditCardListInEditMode];
 
@@ -264,7 +314,15 @@
 // Checks the 'Delete' button is always visible.
 // The button is enabled when a card is selected and disabled when a card is not
 // selected.
-- (void)testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled \
+  DISABLED_testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled
+#else
+#define MAYBE_testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled \
+  testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled
+#endif
+- (void)MAYBE_testToolbarDeleteButtonWithAddPaymentMethodFeatureEnabled {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           SettingsBottomToolbarDeleteButton()]
@@ -289,7 +347,13 @@
 }
 
 // Checks that deleting a card exits from edit mode.
-- (void)testDeletingCreditCard {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testDeletingCreditCard DISABLED_testDeletingCreditCard
+#else
+#define MAYBE_testDeletingCreditCard testDeletingCreditCard
+#endif
+- (void)MAYBE_testDeletingCreditCard {
   NSString* lastDigits = [AutofillAppInterface saveLocalCreditCard];
   [self openCreditCardListInEditMode];
   [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_edit_credit_card_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_edit_credit_card_egtest.mm
index 40fc230..03cf955 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_edit_credit_card_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_credit_card_egtest.mm
@@ -82,7 +82,13 @@
 #pragma mark - Test that all fields on the 'Add Credit Card' screen appear
 
 // Tests that editing the credit card nickname is possible.
-- (void)testValidNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testValidNickname DISABLED_testValidNickname
+#else
+#define MAYBE_testValidNickname testValidNickname
+#endif
+- (void)MAYBE_testValidNickname {
   [self typeNickname:@"Nickname"];
 
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
@@ -94,7 +100,13 @@
 }
 
 // Tests that invalid nicknames are not allowed when editing a card.
-- (void)testInvalidNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testInvalidNickname DISABLED_testInvalidNickname
+#else
+#define MAYBE_testInvalidNickname testInvalidNickname
+#endif
+- (void)MAYBE_testInvalidNickname {
   [self typeNickname:@"1233"];
 
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
@@ -103,7 +115,13 @@
 }
 
 // Tests that clearing a nickname is allowed.
-- (void)testEmptyNickname {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testEmptyNickname DISABLED_testEmptyNickname
+#else
+#define MAYBE_testEmptyNickname testEmptyNickname
+#endif
+- (void)MAYBE_testEmptyNickname {
   [self typeNickname:@"To be removed"];
 
   [[EarlGrey selectElementWithMatcher:NicknameTextField()]
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
index fba1bb10..4c35c2e 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
@@ -215,7 +215,15 @@
 }
 
 // Test that the page for editing Autofill profile details is accessible.
-- (void)testAccessibilityOnAutofillProfileEditPage {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testAccessibilityOnAutofillProfileEditPage \
+  DISABLED_testAccessibilityOnAutofillProfileEditPage
+#else
+#define MAYBE_testAccessibilityOnAutofillProfileEditPage \
+  testAccessibilityOnAutofillProfileEditPage
+#endif
+- (void)MAYBE_testAccessibilityOnAutofillProfileEditPage {
   [AutofillAppInterface saveExampleProfile];
   [self openEditProfile:kProfileLabel];
 
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index 245b436..bdcca1d 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -87,7 +87,15 @@
 
 // Tests that the Account Settings screen is correctly popped if the signed in
 // account is removed while the "Disconnect Account" dialog is up.
-- (void)testSignInPopUpAccountOnDisconnectAccount {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSignInPopUpAccountOnDisconnectAccount \
+  DISABLED_testSignInPopUpAccountOnDisconnectAccount
+#else
+#define MAYBE_testSignInPopUpAccountOnDisconnectAccount \
+  testSignInPopUpAccountOnDisconnectAccount
+#endif
+- (void)MAYBE_testSignInPopUpAccountOnDisconnectAccount {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -111,7 +119,14 @@
 
 // Tests that the Account Settings screen is correctly reloaded when one of
 // the non-primary account is removed.
-- (void)testSignInReloadOnRemoveAccount {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSignInReloadOnRemoveAccount \
+  DISABLED_testSignInReloadOnRemoveAccount
+#else
+#define MAYBE_testSignInReloadOnRemoveAccount testSignInReloadOnRemoveAccount
+#endif
+- (void)MAYBE_testSignInReloadOnRemoveAccount {
   FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
   FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
   [SigninEarlGrey addFakeIdentity:fakeIdentity2];
@@ -137,7 +152,13 @@
 
 // Tests that the Account Settings screen is popped and the user signed out
 // when the account is removed.
-- (void)testSignOutOnRemoveAccount {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSignOutOnRemoveAccount DISABLED_testSignOutOnRemoveAccount
+#else
+#define MAYBE_testSignOutOnRemoveAccount testSignOutOnRemoveAccount
+#endif
+- (void)MAYBE_testSignOutOnRemoveAccount {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
@@ -158,7 +179,15 @@
 
 // Tests that selecting sign-out from a non-managed account keeps the user's
 // synced data.
-- (void)testSignOutFromNonManagedAccountKeepsData {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSignOutFromNonManagedAccountKeepsData \
+  DISABLED_testSignOutFromNonManagedAccountKeepsData
+#else
+#define MAYBE_testSignOutFromNonManagedAccountKeepsData \
+  testSignOutFromNonManagedAccountKeepsData
+#endif
+- (void)MAYBE_testSignOutFromNonManagedAccountKeepsData {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|.
@@ -183,7 +212,15 @@
 
 // Tests that selecting sign-out and clear data from a non-managed user account
 // clears the user's synced data.
-- (void)testSignOutAndClearDataFromNonManagedAccountClearsData {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSignOutAndClearDataFromNonManagedAccountClearsData \
+  DISABLED_testSignOutAndClearDataFromNonManagedAccountClearsData
+#else
+#define MAYBE_testSignOutAndClearDataFromNonManagedAccountClearsData \
+  testSignOutAndClearDataFromNonManagedAccountClearsData
+#endif
+- (void)MAYBE_testSignOutAndClearDataFromNonManagedAccountClearsData {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|.
@@ -213,7 +250,14 @@
 }
 
 // Tests that signing out from a managed user account clears the user's data.
-- (void)testsSignOutFromManagedAccount {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testsSignOutFromManagedAccount \
+  DISABLED_testsSignOutFromManagedAccount
+#else
+#define MAYBE_testsSignOutFromManagedAccount testsSignOutFromManagedAccount
+#endif
+- (void)MAYBE_testsSignOutFromManagedAccount {
   // Sign In |fakeManagedIdentity|.
   [SigninEarlGreyUI
       signinWithFakeIdentity:[SigninEarlGrey fakeManagedIdentity]];
@@ -244,7 +288,15 @@
 // Tests that given two accounts A and B that are available on the device -
 // signing in and out from account A, then signing in to account B, properly
 // identifies the user with account B.
-- (void)testSwitchingAccountsWithClearedData {
+#if !TARGET_IPHONE_SIMULATOR
+// TODO(crbug.com/1177079): Disable for Devices
+#define MAYBE_testSwitchingAccountsWithClearedData \
+  DISABLED_testSwitchingAccountsWithClearedData
+#else
+#define MAYBE_testSwitchingAccountsWithClearedData \
+  testSwitchingAccountsWithClearedData
+#endif
+- (void)MAYBE_testSwitchingAccountsWithClearedData {
   FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
   FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
   [SigninEarlGrey addFakeIdentity:fakeIdentity1];
diff --git a/ios/chrome/browser/ui/start_surface/BUILD.gn b/ios/chrome/browser/ui/start_surface/BUILD.gn
index 36a149dd..7912b6a 100644
--- a/ios/chrome/browser/ui/start_surface/BUILD.gn
+++ b/ios/chrome/browser/ui/start_surface/BUILD.gn
@@ -10,5 +10,26 @@
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
-  deps = [ "//base" ]
+  deps = [
+    "//base",
+    "//ui/base",
+  ]
+}
+
+source_set("start_surface") {
+  sources = [
+    "start_surface_scene_agent.h",
+    "start_surface_scene_agent.mm",
+    "start_surface_util.h",
+    "start_surface_util.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":feature_flags",
+    "//base",
+    "//ios/chrome/browser/ui/main:observing_scene_agent",
+    "//ios/chrome/browser/ui/main:scene_state_header",
+  ]
 }
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_features.h b/ios/chrome/browser/ui/start_surface/start_surface_features.h
index a63b0ce..109f449 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_features.h
+++ b/ios/chrome/browser/ui/start_surface/start_surface_features.h
@@ -10,6 +10,14 @@
 // The feature to enable or disable the Start Surface.
 extern const base::Feature kStartSurface;
 
+// The feature parameter to indicate inactive duration to return to the Start
+// Surface in seconds.
+extern const char kReturnToStartSurfaceInactiveDurationInSeconds[];
+
+// Checks whether the Start Surface should be enabled.
 bool IsStartSurfaceEnabled();
 
+// Returns the inactive duration to show the Start Surface.
+double GetReturnToStartSurfaceDuration();
+
 #endif  // IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_FEATURES_H_.
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_features.mm b/ios/chrome/browser/ui/start_surface/start_surface_features.mm
index c61b9aa..93c3a3a8 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_features.mm
+++ b/ios/chrome/browser/ui/start_surface/start_surface_features.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
+#include "base/metrics/field_trial_params.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -11,6 +12,15 @@
 const base::Feature kStartSurface{"StartSurface",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
+const char kReturnToStartSurfaceInactiveDurationInSeconds[] =
+    "ReturnToStartSurfaceInactiveDurationInSeconds";
+
 bool IsStartSurfaceEnabled() {
   return base::FeatureList::IsEnabled(kStartSurface);
 }
+
+double GetReturnToStartSurfaceDuration() {
+  return base::GetFieldTrialParamByFeatureAsDouble(
+      kStartSurface, kReturnToStartSurfaceInactiveDurationInSeconds,
+      60 * 60 /*default to 1 hour*/);
+}
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.h b/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.h
new file mode 100644
index 0000000..aa30b52
--- /dev/null
+++ b/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.h
@@ -0,0 +1,14 @@
+// Copyright 2021 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 IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_SCENE_AGENT_H_
+#define IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_SCENE_AGENT_H_
+
+#import "ios/chrome/browser/ui/main/observing_scene_state_agent.h"
+
+// A scene agent for the Start Surface.
+@interface StartSurfaceSceneAgent : ObservingSceneAgent
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_SCENE_AGENT_H_
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.mm b/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.mm
new file mode 100644
index 0000000..cb611e03
--- /dev/null
+++ b/ios/chrome/browser/ui/start_surface/start_surface_scene_agent.mm
@@ -0,0 +1,26 @@
+// Copyright 2021 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/start_surface/start_surface_scene_agent.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation StartSurfaceSceneAgent
+
+#pragma mark - SceneStateObserver
+
+- (void)sceneState:(SceneState*)sceneState
+    transitionedToActivationLevel:(SceneActivationLevel)level {
+  if (level == SceneActivationLevelBackground) {
+    // TODO(crbug.com/1173160): Consider when to clear the session object since
+    // Chrome may be closed without transiting to background, e.g. device power
+    // off, then the previous session object is staled.
+    SetStartSurfaceSessionObjectForSceneState(sceneState);
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_util.h b/ios/chrome/browser/ui/start_surface/start_surface_util.h
new file mode 100644
index 0000000..d5bba69
--- /dev/null
+++ b/ios/chrome/browser/ui/start_surface/start_surface_util.h
@@ -0,0 +1,16 @@
+// Copyright 2021 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 IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_UTIL_H_
+#define IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_UTIL_H_
+
+#import "ios/chrome/browser/ui/main/scene_state.h"
+
+// Checks whether the Start Surface should be shown for the given scene state.
+bool ShouldShowStartSurfaceForSceneState(SceneState* sceneState);
+
+// Sets the session related objects for the Start Surface.
+void SetStartSurfaceSessionObjectForSceneState(SceneState* sceneState);
+
+#endif  // IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_UTIL_H_.
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_util.mm b/ios/chrome/browser/ui/start_surface/start_surface_util.mm
new file mode 100644
index 0000000..c98607a
--- /dev/null
+++ b/ios/chrome/browser/ui/start_surface/start_surface_util.mm
@@ -0,0 +1,48 @@
+// Copyright 2021 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/start_surface/start_surface_util.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// The key to store the timestamp when the scene enters into background.
+NSString* kStartSurfaceSceneEnterIntoBackgroundTime =
+    @"StartSurfaceSceneEnterIntoBackgroundTime";
+
+}  // namespace
+
+bool ShouldShowStartSurfaceForSceneState(SceneState* sceneState) {
+  if (!IsStartSurfaceEnabled()) {
+    return NO;
+  }
+
+  NSDate* timestamp = (NSDate*)[sceneState
+      sessionObjectForKey:kStartSurfaceSceneEnterIntoBackgroundTime];
+  if (timestamp == nil || [[NSDate date] timeIntervalSinceDate:timestamp] <
+                              GetReturnToStartSurfaceDuration()) {
+    return NO;
+  }
+
+  if (sceneState.presentingFirstRunUI || sceneState.presentingModalOverlay ||
+      sceneState.startupHadExternalIntent || sceneState.pendingUserActivity ||
+      sceneState.incognitoContentVisible) {
+    return NO;
+  }
+
+  return YES;
+}
+
+void SetStartSurfaceSessionObjectForSceneState(SceneState* sceneState) {
+  if (!IsStartSurfaceEnabled()) {
+    return;
+  }
+
+  [sceneState setSessionObject:[NSDate date]
+                        forKey:kStartSurfaceSceneEnterIntoBackgroundTime];
+}
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index ed8db72..d081488d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-b8de1b4cdd6a35913f42e169de5310fda618ddf2
\ No newline at end of file
+cc1ae3b752b97191e19d2e4d7ab25b7b318bb42e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 36db25f..354de70 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-9a853bfc5db4617dce5bd34a6df88e66ed4acbde
\ No newline at end of file
+bd34b10a2b3c387cfba4faa855fd30c42410b617
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 9ee892e..6cdc8ec 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-702c6e398942a19f78609874cb209eb9d878e496
\ No newline at end of file
+b201a526d008461a2dc14664e874a6b852f81d45
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 7d4cf31..44036741 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-2d781e223a4a32b65d630fab358a7561dd12c042
\ No newline at end of file
+ecb04848c777b30975232b24e3139ef941f8b810
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 98d2b34..a1d6c596 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-d005ca1b4e45ed8168898cb4c5f7083aab3c65d5
\ No newline at end of file
+0be4694310f67b620c5d578dd8a70bce8af4b503
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index eb60bde..01e4443 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-b28105db86a9c331b65476dfb097921d2b019871
\ No newline at end of file
+7f4306a40ba90ad2b28a35401432f725befc7147
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 00dd4be..0cfae55 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-23a47068a930eafe296219513ec8da22155078c6
\ No newline at end of file
+1b245ff90aa3d55a4264af24191235f2b8fbb509
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 47c3d64..3dc65a56 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-53fe2415d0a445a3407cc55a761ed9bb145750ae
\ No newline at end of file
+b281d533981e6a27c7c9f26e54fd95e1429ac667
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index c0f8e08e..9383421 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-33c787e880ef5c04c19b709b3b4b85a799e4d93e
\ No newline at end of file
+ad1902081171a8b89e72ad529b77d2f40d0ac06f
\ No newline at end of file
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.h b/ios/web/js_features/context_menu/context_menu_java_script_feature.h
index 2e0ca85..3ea3784f 100644
--- a/ios/web/js_features/context_menu/context_menu_java_script_feature.h
+++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.h
@@ -9,7 +9,6 @@
 
 #include <map>
 #include <string>
-#include <vector>
 
 #include "base/callback.h"
 #include "base/supports_user_data.h"
@@ -45,7 +44,7 @@
                          ElementDetailsCallback callback);
 
   // JavaScriptFeature:
-  std::vector<std::string> GetScriptMessageHandlerNames() const override;
+  base::Optional<std::string> GetScriptMessageHandlerName() const override;
   void ScriptMessageReceived(BrowserState* browser_state,
                              WKScriptMessage* message) override;
 
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
index 36d8f2ba..1429f978 100644
--- a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
+++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
@@ -78,9 +78,9 @@
   CallJavaScriptFunction(main_frame, "findElementAtPoint", parameters);
 }
 
-std::vector<std::string>
-ContextMenuJavaScriptFeature::GetScriptMessageHandlerNames() const {
-  return {kFindElementResultHandlerName};
+base::Optional<std::string>
+ContextMenuJavaScriptFeature::GetScriptMessageHandlerName() const {
+  return kFindElementResultHandlerName;
 }
 
 void ContextMenuJavaScriptFeature::ScriptMessageReceived(
diff --git a/ios/web/js_features/window_error/window_error_java_script_feature.h b/ios/web/js_features/window_error/window_error_java_script_feature.h
index b711a8e..b9461aa1 100644
--- a/ios/web/js_features/window_error/window_error_java_script_feature.h
+++ b/ios/web/js_features/window_error/window_error_java_script_feature.h
@@ -49,7 +49,7 @@
 
  private:
   // JavaScriptFeature:
-  std::vector<std::string> GetScriptMessageHandlerNames() const override;
+  base::Optional<std::string> GetScriptMessageHandlerName() const override;
   void ScriptMessageReceived(BrowserState* browser_state,
                              WKScriptMessage* message) override;
 
diff --git a/ios/web/js_features/window_error/window_error_java_script_feature.mm b/ios/web/js_features/window_error/window_error_java_script_feature.mm
index 01c1caa..ecec9a9c 100644
--- a/ios/web/js_features/window_error/window_error_java_script_feature.mm
+++ b/ios/web/js_features/window_error/window_error_java_script_feature.mm
@@ -45,9 +45,9 @@
 }
 WindowErrorJavaScriptFeature::~WindowErrorJavaScriptFeature() = default;
 
-std::vector<std::string>
-WindowErrorJavaScriptFeature::GetScriptMessageHandlerNames() const {
-  return {kWindowErrorResultHandlerName};
+base::Optional<std::string>
+WindowErrorJavaScriptFeature::GetScriptMessageHandlerName() const {
+  return kWindowErrorResultHandlerName;
 }
 
 void WindowErrorJavaScriptFeature::ScriptMessageReceived(
diff --git a/ios/web/js_messaging/java_script_content_world.mm b/ios/web/js_messaging/java_script_content_world.mm
index 6375fa8..d5cc9c0 100644
--- a/ios/web/js_messaging/java_script_content_world.mm
+++ b/ios/web/js_messaging/java_script_content_world.mm
@@ -126,26 +126,31 @@
     [user_content_controller_ addUserScript:user_script];
   }
 
-  // Setup Javascript message callbacks.
-  for (auto handlers_by_name : feature->GetScriptMessageHandlers()) {
+  // Setup Javascript message callback.
+  auto optional_handler_name = feature->GetScriptMessageHandlerName();
+  if (optional_handler_name) {
+    auto handler = feature->GetScriptMessageHandler();
+    DCHECK(handler);
+
+    NSString* handler_name =
+        base::SysUTF8ToNSString(optional_handler_name.value());
+
     std::unique_ptr<ScopedWKScriptMessageHandler> script_message_handler;
 
 #if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
     if (@available(iOS 14, *)) {
       if (content_world_) {
         script_message_handler = std::make_unique<ScopedWKScriptMessageHandler>(
-            user_content_controller_,
-            base::SysUTF8ToNSString(handlers_by_name.first), content_world_,
-            base::BindRepeating(handlers_by_name.second, browser_state_));
+            user_content_controller_, handler_name, content_world_,
+            base::BindRepeating(handler.value(), browser_state_));
       }
     }
 #endif  // defined(__IPHONE14_0)
 
     if (!script_message_handler.get()) {
       script_message_handler = std::make_unique<ScopedWKScriptMessageHandler>(
-          user_content_controller_,
-          base::SysUTF8ToNSString(handlers_by_name.first),
-          base::BindRepeating(handlers_by_name.second, browser_state_));
+          user_content_controller_, handler_name,
+          base::BindRepeating(handler.value(), browser_state_));
     }
     script_message_handlers_[feature] = std::move(script_message_handler);
   }
diff --git a/ios/web/js_messaging/java_script_feature.mm b/ios/web/js_messaging/java_script_feature.mm
index 0c1db3c61..6a637ff82 100644
--- a/ios/web/js_messaging/java_script_feature.mm
+++ b/ios/web/js_messaging/java_script_feature.mm
@@ -132,21 +132,19 @@
   return dependent_features_;
 }
 
-std::vector<std::string> JavaScriptFeature::GetScriptMessageHandlerNames()
+base::Optional<std::string> JavaScriptFeature::GetScriptMessageHandlerName()
     const {
-  return {};
+  return base::nullopt;
 }
 
-std::map<std::string, JavaScriptFeature::ScriptMessageHandler>
-JavaScriptFeature::GetScriptMessageHandlers() const {
-  auto handler = base::BindRepeating(&JavaScriptFeature::ScriptMessageReceived,
-                                     weak_factory_.GetWeakPtr());
-  auto handlers =
-      std::map<std::string, JavaScriptFeature::ScriptMessageHandler>();
-  for (auto handler_name : GetScriptMessageHandlerNames()) {
-    handlers[handler_name] = handler;
+base::Optional<JavaScriptFeature::ScriptMessageHandler>
+JavaScriptFeature::GetScriptMessageHandler() const {
+  if (!GetScriptMessageHandlerName()) {
+    return base::nullopt;
   }
-  return handlers;
+
+  return base::BindRepeating(&JavaScriptFeature::ScriptMessageReceived,
+                             weak_factory_.GetWeakPtr());
 }
 
 void JavaScriptFeature::ScriptMessageReceived(BrowserState* browser_state,
diff --git a/ios/web/public/js_messaging/java_script_feature.h b/ios/web/public/js_messaging/java_script_feature.h
index 74f900b..c7374ee 100644
--- a/ios/web/public/js_messaging/java_script_feature.h
+++ b/ios/web/public/js_messaging/java_script_feature.h
@@ -11,6 +11,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 
 @class NSString;
 @class WKScriptMessage;
@@ -131,17 +132,16 @@
   virtual const std::vector<const JavaScriptFeature*> GetDependentFeatures()
       const;
 
-  // Returns the script message handler names which this feature will receive
-  // messages from JavaScript. Returning an empty vector will not register any
-  // handlers.
-  virtual std::vector<std::string> GetScriptMessageHandlerNames() const;
+  // Returns the script message handler name which this feature will receive
+  // messages from JavaScript. Returning null will not register any handler.
+  virtual base::Optional<std::string> GetScriptMessageHandlerName() const;
 
   using ScriptMessageHandler =
       base::RepeatingCallback<void(BrowserState* browser_state,
                                    WKScriptMessage* message)>;
-  // Returns the names from |GetScriptMessageHandlerNames| mapped to handler
-  // callbacks.
-  std::map<std::string, ScriptMessageHandler> GetScriptMessageHandlers() const;
+  // Returns the script message handler callback if
+  // |GetScriptMessageHandlerName()| returns a handler name.
+  base::Optional<ScriptMessageHandler> GetScriptMessageHandler() const;
 
   JavaScriptFeature(const JavaScriptFeature&) = delete;
 
@@ -159,7 +159,7 @@
       base::OnceCallback<void(const base::Value*)> callback,
       base::TimeDelta timeout);
 
-  // Callback for script messages registered through |GetScriptMessageHandlers|.
+  // Callback for script messages registered through |GetScriptMessageHandler|.
   // Called when a web view associated with |browser_state| sent |message|.
   virtual void ScriptMessageReceived(BrowserState* browser_state,
                                      WKScriptMessage* message);
diff --git a/ios/web/test/fakes/fake_java_script_feature.h b/ios/web/test/fakes/fake_java_script_feature.h
index 554d6f2..7d0290c 100644
--- a/ios/web/test/fakes/fake_java_script_feature.h
+++ b/ios/web/test/fakes/fake_java_script_feature.h
@@ -57,7 +57,7 @@
 
  private:
   // JavaScriptFeature:
-  std::vector<std::string> GetScriptMessageHandlerNames() const override;
+  base::Optional<std::string> GetScriptMessageHandlerName() const override;
   void ScriptMessageReceived(BrowserState* browser_state,
                              WKScriptMessage* message) override;
 
diff --git a/ios/web/test/fakes/fake_java_script_feature.mm b/ios/web/test/fakes/fake_java_script_feature.mm
index 12a753d..478ded8 100644
--- a/ios/web/test/fakes/fake_java_script_feature.mm
+++ b/ios/web/test/fakes/fake_java_script_feature.mm
@@ -78,9 +78,9 @@
                          base::TimeDelta::FromSeconds(kGetErrorCountTimeout));
 }
 
-std::vector<std::string> FakeJavaScriptFeature::GetScriptMessageHandlerNames()
+base::Optional<std::string> FakeJavaScriptFeature::GetScriptMessageHandlerName()
     const {
-  return {std::string(kFakeJavaScriptFeatureScriptHandlerName)};
+  return std::string(kFakeJavaScriptFeatureScriptHandlerName);
 }
 
 void FakeJavaScriptFeature::ScriptMessageReceived(BrowserState* browser_state,
diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc
index 1537642..d2db77cd6 100644
--- a/ipc/ipc_channel_mojo.cc
+++ b/ipc/ipc_channel_mojo.cc
@@ -27,6 +27,7 @@
 #include "ipc/ipc_mojo_handle_attachment.h"
 #include "ipc/native_handle_type_converters.h"
 #include "ipc/trace_ipc_message.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc
index 113f989..a6c8e9a 100644
--- a/ipc/ipc_sync_message_filter.cc
+++ b/ipc/ipc_sync_message_filter.cc
@@ -13,7 +13,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_sync_message.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/sync_handle_registry.h"
 
 namespace IPC {
diff --git a/media/audio/audio_opus_encoder.cc b/media/audio/audio_opus_encoder.cc
index 1ee0f8b5..52f89fd 100644
--- a/media/audio/audio_opus_encoder.cc
+++ b/media/audio/audio_opus_encoder.cc
@@ -220,15 +220,18 @@
   // timestamp for the next audio sample. It means there is a gap/overlap,
   // if it's big enough we flash and start anew, otherwise we ignore it.
   auto capture_ts = ComputeTimestamp(audio_bus.frames(), capture_time);
-  auto end_of_existing_buffer_ts =
-      next_timestamp_ +
-      AudioTimestampHelper::FramesToTime(fifo_.queued_frames(),
-                                         audio_input_params().sample_rate());
+  auto existing_buffer_duration = AudioTimestampHelper::FramesToTime(
+      fifo_.queued_frames(), audio_input_params().sample_rate());
+  auto end_of_existing_buffer_ts = next_timestamp_ + existing_buffer_duration;
   base::TimeDelta gap = (capture_ts - end_of_existing_buffer_ts).magnitude();
   constexpr base::TimeDelta max_gap = base::TimeDelta::FromMilliseconds(1);
   if (gap > max_gap) {
-    DLOG(ERROR) << "Large gap in sound. Forced flush. "
-                << "Gap/overlap duration: " << gap;
+    DLOG(ERROR) << "Large gap in sound. Forced flush."
+                << " Gap/overlap duration: " << gap
+                << " capture_ts: " << capture_ts
+                << " next_timestamp_: " << next_timestamp_
+                << " existing_buffer_duration: " << existing_buffer_duration
+                << " end_of_existing_buffer_ts: " << end_of_existing_buffer_ts;
     FlushImpl();
     next_timestamp_ = capture_ts;
   }
diff --git a/media/gpu/android/direct_shared_image_video_provider.cc b/media/gpu/android/direct_shared_image_video_provider.cc
index 2a70088..11efbbe4 100644
--- a/media/gpu/android/direct_shared_image_video_provider.cc
+++ b/media/gpu/android/direct_shared_image_video_provider.cc
@@ -64,10 +64,11 @@
 // TODO(liberato): add a thread hop to create the default texture owner, but
 // not as part of this class.  just post something from VideoFrameFactory.
 void DirectSharedImageVideoProvider::Initialize(GpuInitCB gpu_init_cb) {
-  // Note that we do not BindToCurrentLoop |gpu_init_cb|, since it is supposed
-  // to be called on the gpu main thread, which is somewhat hacky.
-  gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::Initialize,
-                    std::move(gpu_init_cb));
+  // Note that we do use not `AsyncCall()` + `Then()` to call `gpu_init_cb`,
+  // since it is supposed to be called on the gpu main thread, which is somewhat
+  // hacky.
+  gpu_factory_.AsyncCall(&GpuSharedImageVideoFactory::Initialize)
+      .WithArgs(std::move(gpu_init_cb));
 }
 
 void DirectSharedImageVideoProvider::RequestImage(
@@ -83,9 +84,11 @@
   // group anyway.  The thing that owns buffer management is all we really
   // care about, and that doesn't have anything to do with GLImage.
 
-  gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::CreateImage,
-                    BindToCurrentLoop(std::move(cb)), spec,
-                    std::move(texture_owner));
+  // Note: `cb` is only run on successful creation, so this does not use
+  // `AsyncCall()` + `Then()` to chain the callbacks.
+  gpu_factory_.AsyncCall(&GpuSharedImageVideoFactory::CreateImage)
+      .WithArgs(BindToCurrentLoop(std::move(cb)), spec,
+                std::move(texture_owner));
 }
 
 GpuSharedImageVideoFactory::GpuSharedImageVideoFactory(
diff --git a/media/gpu/android/frame_info_helper.cc b/media/gpu/android/frame_info_helper.cc
index cf18f96..6d4b209 100644
--- a/media/gpu/android/frame_info_helper.cc
+++ b/media/gpu/android/frame_info_helper.cc
@@ -196,8 +196,8 @@
             base::BindOnce(&FrameInfoHelperImpl::OnFrameInfoReady,
                            weak_factory_.GetWeakPtr()));
 
-        on_gpu_.Post(FROM_HERE, &OnGpu::GetFrameInfo,
-                     std::move(request.buffer_renderer), std::move(cb));
+        on_gpu_.AsyncCall(&OnGpu::GetFrameInfo)
+            .WithArgs(std::move(request.buffer_renderer), std::move(cb));
         // We didn't complete this request quite yet, so we can't process queue
         // any further.
         break;
diff --git a/media/gpu/android/maybe_render_early_manager.cc b/media/gpu/android/maybe_render_early_manager.cc
index 84c2b9c..3ba450a3 100644
--- a/media/gpu/android/maybe_render_early_manager.cc
+++ b/media/gpu/android/maybe_render_early_manager.cc
@@ -89,18 +89,18 @@
 
     // Give the image group to |gpu_impl_|.  Note that we don't drop our ref to
     // |image_group| on this thread.  It can only be constructed here.
-    gpu_impl_.Post(FROM_HERE, &GpuMaybeRenderEarlyImpl::SetCodecImageGroup,
-                   std::move(image_group));
+    gpu_impl_.AsyncCall(&GpuMaybeRenderEarlyImpl::SetCodecImageGroup)
+        .WithArgs(std::move(image_group));
   }
 
   void AddCodecImage(
       scoped_refptr<CodecImageHolder> codec_image_holder) override {
-    gpu_impl_.Post(FROM_HERE, &GpuMaybeRenderEarlyImpl::AddCodecImage,
-                   std::move(codec_image_holder));
+    gpu_impl_.AsyncCall(&GpuMaybeRenderEarlyImpl::AddCodecImage)
+        .WithArgs(std::move(codec_image_holder));
   }
 
   void MaybeRenderEarly() override {
-    gpu_impl_.Post(FROM_HERE, &GpuMaybeRenderEarlyImpl::MaybeRenderEarly);
+    gpu_impl_.AsyncCall(&GpuMaybeRenderEarlyImpl::MaybeRenderEarly);
   }
 
  private:
diff --git a/media/gpu/android/pooled_shared_image_video_provider.cc b/media/gpu/android/pooled_shared_image_video_provider.cc
index f46a8ca0..a4b10ff 100644
--- a/media/gpu/android/pooled_shared_image_video_provider.cc
+++ b/media/gpu/android/pooled_shared_image_video_provider.cc
@@ -122,11 +122,11 @@
     const gpu::SyncToken& sync_token) {
   // An image has been returned to us.  Wait for |sync_token| and then send it
   // to ProcessFreePooledImage to re-use / pool / delete.
-  gpu_helper_.Post(FROM_HERE, &GpuHelper::OnImageReturned, sync_token,
-                   pooled_image->record.codec_image_holder,
-                   BindToCurrentLoop(base::BindOnce(
-                       &PooledSharedImageVideoProvider::ProcessFreePooledImage,
-                       weak_factory_.GetWeakPtr(), pooled_image)));
+  gpu_helper_.AsyncCall(&GpuHelper::OnImageReturned)
+      .WithArgs(sync_token, pooled_image->record.codec_image_holder,
+                BindToCurrentLoop(base::BindOnce(
+                    &PooledSharedImageVideoProvider::ProcessFreePooledImage,
+                    weak_factory_.GetWeakPtr(), pooled_image)));
 }
 
 void PooledSharedImageVideoProvider::ProcessFreePooledImage(
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc
index 4fa6fd72..d06737ca 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -165,10 +165,10 @@
   // device for decoding.  Sharing seems not to work very well.  Otherwise, we
   // would create the texture with KEYED_MUTEX and NTHANDLE, then send along
   // a handle that we get from |texture| as an IDXGIResource1.
-  gpu_resources_.Post(FROM_HERE, &GpuResources::Init, std::move(get_helper_cb),
-                      std::move(mailboxes), GL_TEXTURE_EXTERNAL_OES, size_,
-                      textures_per_picture, texture_formats, pixel_format_,
-                      texture, array_slice);
+  gpu_resources_.AsyncCall(&GpuResources::Init)
+      .WithArgs(std::move(get_helper_cb), std::move(mailboxes),
+                GL_TEXTURE_EXTERNAL_OES, size_, textures_per_picture,
+                texture_formats, pixel_format_, texture, array_slice);
   return OkStatus();
 }
 
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 669ae99..d0468591 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -453,8 +453,8 @@
   // the originals on some other thread.
   // Important but subtle note: base::Bind will copy |config_| since it's a
   // const ref.
-  impl_.Post(FROM_HERE, &D3D11VideoDecoderImpl::Initialize,
-             BindToCurrentLoop(std::move(impl_init_cb)));
+  impl_.AsyncCall(&D3D11VideoDecoderImpl::Initialize)
+      .WithArgs(BindToCurrentLoop(std::move(impl_init_cb)));
 }
 
 void D3D11VideoDecoder::AddLifetimeProgressionStage(
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 5a3d8c1..b91e21c 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -448,6 +448,25 @@
                           public testing::WithParamInterface<PlaybackTestData> {
 };
 
+TEST_P(BasicPlaybackTest, PlayToEnd) {
+  PlaybackTestData data = GetParam();
+
+  ASSERT_EQ(PIPELINE_OK, Start(data.filename, kUnreliableDuration));
+  EXPECT_EQ(data.start_time_ms, demuxer_->GetStartTime().InMilliseconds());
+  EXPECT_EQ(data.duration_ms, pipeline_->GetMediaDuration().InMilliseconds());
+
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+const PlaybackTestData kOpenCodecsTests[] = {{"bear-vp9-i422.webm", 0, 2736}};
+
+INSTANTIATE_TEST_SUITE_P(OpenCodecs,
+                         BasicPlaybackTest,
+                         testing::ValuesIn(kOpenCodecsTests));
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+
 class BasicMSEPlaybackTest
     : public ::testing::WithParamInterface<MSEPlaybackTestData>,
       public PipelineIntegrationTest {
@@ -475,29 +494,10 @@
   }
 };
 
-TEST_P(BasicPlaybackTest, PlayToEnd) {
-  PlaybackTestData data = GetParam();
-
-  ASSERT_EQ(PIPELINE_OK, Start(data.filename, kUnreliableDuration));
-  EXPECT_EQ(data.start_time_ms, demuxer_->GetStartTime().InMilliseconds());
-  EXPECT_EQ(data.duration_ms, pipeline_->GetMediaDuration().InMilliseconds());
-
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
 TEST_P(BasicMSEPlaybackTest, PlayToEnd) {
   PlayToEnd();
 }
 
-const PlaybackTestData kOpenCodecsTests[] = {{"bear-vp9-i422.webm", 0, 2736}};
-
-INSTANTIATE_TEST_SUITE_P(OpenCodecs,
-                         BasicPlaybackTest,
-                         testing::ValuesIn(kOpenCodecsTests));
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-
 const PlaybackTestData kADTSTests[] = {
     {"bear-audio-main-aac.aac", 0, 2708},
     {"bear-audio-lc-aac.aac", 0, 2791},
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 3e04090..af1b7eb 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -57,8 +57,6 @@
     "lib/bindings_internal.h",
     "lib/buffer.cc",
     "lib/buffer.h",
-    "lib/fixed_buffer.cc",
-    "lib/fixed_buffer.h",
     "lib/handle_serialization.cc",
     "lib/handle_serialization.h",
     "lib/hash_util.h",
@@ -128,7 +126,6 @@
 
 component("bindings") {
   sources = [
-    "associated_interface_ptr.h",
     "associated_interface_ptr_info.h",
     "associated_interface_request.h",
     "associated_receiver.h",
@@ -147,7 +144,6 @@
     "interface_ptr.h",
     "interface_ptr_info.h",
     "interface_request.h",
-    "lib/associated_interface_ptr.cc",
     "lib/associated_interface_ptr_state.cc",
     "lib/associated_interface_ptr_state.h",
     "lib/associated_receiver.cc",
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h
deleted file mode 100644
index 956138c4..0000000
--- a/mojo/public/cpp/bindings/associated_interface_ptr.h
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
-
-#include <stdint.h>
-
-#include <cstddef>
-#include <string>
-#include <utility>
-
-#include "base/callback.h"
-#include "base/check.h"
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
-#include "mojo/public/cpp/bindings/associated_interface_request.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
-#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-
-namespace mojo {
-
-// DEPRECATED: Do not introduce new uses of this type. Instead use the
-// AssociatedRemote type defined in associated_remote.h.
-//
-// Represents the client side of an associated interface. It is similar to
-// InterfacePtr, except that it doesn't own a message pipe handle.
-template <typename Interface>
-class AssociatedInterfacePtr {
- public:
-  using InterfaceType = Interface;
-  using PtrInfoType = AssociatedInterfacePtrInfo<Interface>;
-  using Proxy = typename Interface::Proxy_;
-
-  // Constructs an unbound AssociatedInterfacePtr.
-  AssociatedInterfacePtr() {}
-  AssociatedInterfacePtr(std::nullptr_t) {}
-
-  AssociatedInterfacePtr(AssociatedInterfacePtr&& other) {
-    internal_state_.Swap(&other.internal_state_);
-  }
-
-  explicit AssociatedInterfacePtr(PtrInfoType&& info) { Bind(std::move(info)); }
-
-  AssociatedInterfacePtr& operator=(AssociatedInterfacePtr&& other) {
-    reset();
-    internal_state_.Swap(&other.internal_state_);
-    return *this;
-  }
-
-  // Assigning nullptr to this class causes it to closes the associated
-  // interface (if any) and returns the pointer to the unbound state.
-  AssociatedInterfacePtr& operator=(std::nullptr_t) {
-    reset();
-    return *this;
-  }
-
-  ~AssociatedInterfacePtr() {}
-
-  // Sets up this object as the client side of an associated interface.
-  // Calling with an invalid |info| has the same effect as reset(). In this
-  // case, the AssociatedInterfacePtr is not considered as bound.
-  //
-  // Optionally, |runner| is a SequencedTaskRunner bound to the current sequence
-  // on which all callbacks and connection error notifications will be
-  // dispatched. It is only useful to specify this to use a different
-  // SequencedTaskRunner than SequencedTaskRunnerHandle::Get().
-  //
-  // NOTE: The corresponding AssociatedInterfaceRequest must be sent over
-  // another interface before using this object to make calls. Please see the
-  // comments of MakeRequest(AssociatedInterfacePtr<Interface>*) for more
-  // details.
-  void Bind(AssociatedInterfacePtrInfo<Interface> info,
-            scoped_refptr<base::SequencedTaskRunner> runner = nullptr) {
-    reset();
-
-    if (info.is_valid())
-      internal_state_.Bind(std::move(info), std::move(runner));
-  }
-
-  bool is_bound() const { return internal_state_.is_bound(); }
-
-  Proxy* get() const { return internal_state_.instance(); }
-
-  // Functions like a pointer to Interface. Must already be bound.
-  Proxy* operator->() const { return get(); }
-  Proxy& operator*() const { return *get(); }
-
-  // Returns the version number of the interface that the remote side supports.
-  uint32_t version() const { return internal_state_.version(); }
-
-  // Queries the max version that the remote side supports. On completion, the
-  // result will be returned as the input of |callback|. The version number of
-  // this object will also be updated.
-  void QueryVersion(base::OnceCallback<void(uint32_t)> callback) {
-    internal_state_.QueryVersion(std::move(callback));
-  }
-
-  // If the remote side doesn't support the specified version, it will close the
-  // associated interface asynchronously. This does nothing if it's already
-  // known that the remote side supports the specified version, i.e., if
-  // |version <= this->version()|.
-  //
-  // After calling RequireVersion() with a version not supported by the remote
-  // side, all subsequent calls to interface methods will be ignored.
-  void RequireVersion(uint32_t version) {
-    internal_state_.RequireVersion(version);
-  }
-
-  // Sends a message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting() { internal_state_.FlushForTesting(); }
-
-  // Closes the associated interface (if any) and returns the pointer to the
-  // unbound state.
-  void reset() {
-    State doomed;
-    internal_state_.Swap(&doomed);
-  }
-
-  // Similar to the method above, but also specifies a disconnect reason.
-  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
-    if (internal_state_.is_bound())
-      internal_state_.CloseWithReason(custom_reason, description);
-    reset();
-  }
-
-  // Indicates whether an error has been encountered. If true, method calls made
-  // on this interface will be dropped (and may already have been dropped).
-  bool encountered_error() const { return internal_state_.encountered_error(); }
-
-  // Registers a handler to receive error notifications.
-  //
-  // This method may only be called after the AssociatedInterfacePtr has been
-  // bound.
-  void set_connection_error_handler(base::OnceClosure error_handler) {
-    internal_state_.set_connection_error_handler(std::move(error_handler));
-  }
-
-  void set_connection_error_with_reason_handler(
-      ConnectionErrorWithReasonCallback error_handler) {
-    internal_state_.set_connection_error_with_reason_handler(
-        std::move(error_handler));
-  }
-
-  // Unbinds and returns the associated interface pointer information which
-  // could be used to setup an AssociatedInterfacePtr again. This method may be
-  // used to move the proxy to a different sequence.
-  //
-  // It is an error to call PassInterface() while there are pending responses.
-  // TODO: fix this restriction, it's not always obvious when there is a
-  // pending response.
-  AssociatedInterfacePtrInfo<Interface> PassInterface() {
-    DCHECK(!internal_state_.has_pending_callbacks());
-    State state;
-    internal_state_.Swap(&state);
-
-    return state.PassInterface();
-  }
-
-  // DO NOT USE. Exposed only for internal use and for testing.
-  internal::AssociatedInterfacePtrState<Interface>* internal_state() {
-    return &internal_state_;
-  }
-
-  // Allow AssociatedInterfacePtr<> to be used in boolean expressions.
-  explicit operator bool() const { return internal_state_.is_bound(); }
-
- private:
-  typedef internal::AssociatedInterfacePtrState<Interface> State;
-  mutable State internal_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr);
-};
-
-// Creates an associated interface. The returned request is supposed to be sent
-// over another interface (either associated or non-associated).
-//
-// NOTE: |ptr| must NOT be used to make calls before the request is sent.
-// Violating that will lead to crash. On the other hand, as soon as the request
-// is sent, |ptr| is usable. There is no need to wait until the request is bound
-// to an implementation at the remote side.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequest(
-    AssociatedInterfacePtr<Interface>* ptr,
-    scoped_refptr<base::SequencedTaskRunner> runner = nullptr) {
-  AssociatedInterfacePtrInfo<Interface> ptr_info;
-  auto request = MakeRequest(&ptr_info);
-  ptr->Bind(std::move(ptr_info), std::move(runner));
-  return request;
-}
-
-// Creates an associated interface. One of the two endpoints is supposed to be
-// sent over another interface (either associated or non-associated); while the
-// other is used locally.
-//
-// NOTE: If |ptr_info| is used locally and bound to an AssociatedInterfacePtr,
-// the interface pointer must NOT be used to make calls before the request is
-// sent. Please see NOTE of the previous function for more details.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequest(
-    AssociatedInterfacePtrInfo<Interface>* ptr_info) {
-  ScopedInterfaceEndpointHandle handle0;
-  ScopedInterfaceEndpointHandle handle1;
-  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0,
-                                                              &handle1);
-
-  ptr_info->set_handle(std::move(handle0));
-  ptr_info->set_version(0);
-
-  return AssociatedInterfaceRequest<Interface>(std::move(handle1));
-}
-
-// Like MakeRequest() above, but it creates a dedicated message pipe. The
-// returned request can be bound directly to an implementation, without being
-// first passed through a message pipe endpoint.
-//
-// This function has two main uses:
-//
-//  * In testing, where the returned request is bound to e.g. a mock and there
-//    are no other interfaces involved.
-//
-//  * When discarding messages sent on an interface, which can be done by
-//    discarding the returned request.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequestAssociatedWithDedicatedPipe(
-    AssociatedInterfacePtr<Interface>* ptr) {
-  MessagePipe pipe;
-  scoped_refptr<internal::MultiplexRouter> router0 =
-      new internal::MultiplexRouter(
-          std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE,
-          false, base::SequencedTaskRunnerHandle::Get());
-  scoped_refptr<internal::MultiplexRouter> router1 =
-      new internal::MultiplexRouter(
-          std::move(pipe.handle1), internal::MultiplexRouter::MULTI_INTERFACE,
-          true, base::SequencedTaskRunnerHandle::Get());
-
-  ScopedInterfaceEndpointHandle endpoint0, endpoint1;
-  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0,
-                                                              &endpoint1);
-  InterfaceId id = router1->AssociateInterface(std::move(endpoint0));
-  endpoint0 = router0->CreateLocalEndpointHandle(id);
-
-  ptr->Bind(AssociatedInterfacePtrInfo<Interface>(std::move(endpoint0),
-                                                  Interface::Version_));
-  return AssociatedInterfaceRequest<Interface>(std::move(endpoint1));
-}
-
-// |handle| is supposed to be the request of an associated interface. This
-// method associates the interface with a dedicated, disconnected message pipe.
-// That way, the corresponding associated interface pointer of |handle| can
-// safely make calls (although those calls are silently dropped).
-COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
-void AssociateWithDisconnectedPipe(ScopedInterfaceEndpointHandle handle);
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/associated_receiver.h b/mojo/public/cpp/bindings/associated_receiver.h
index 3e0d68c0..92b4c0e 100644
--- a/mojo/public/cpp/bindings/associated_receiver.h
+++ b/mojo/public/cpp/bindings/associated_receiver.h
@@ -293,6 +293,13 @@
   base::WeakPtrFactory<AssociatedReceiver> weak_ptr_factory_{this};
 };
 
+// Associates |handle| with a dedicated and disconnected message pipe.
+// Generally, |handle| should be the receiving side of an entangled
+// AssociatedReceiver/AssociatedRemote pair, which allows the AssociatedRemote
+// to be used to make calls that will be silently dropped.
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
+void AssociateWithDisconnectedPipe(ScopedInterfaceEndpointHandle handle);
+
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_RECEIVER_H_
diff --git a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
index d5fae3b5..46a4908 100644
--- a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
+++ b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
@@ -15,8 +15,6 @@
 template <typename Interface>
 class InterfaceRequest;
 template <typename Interface>
-class AssociatedInterfacePtr;
-template <typename Interface>
 class AssociatedInterfacePtrInfo;
 template <typename Interface>
 class AssociatedInterfaceRequest;
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr.cc b/mojo/public/cpp/bindings/lib/associated_interface_ptr.cc
deleted file mode 100644
index 453e47a..0000000
--- a/mojo/public/cpp/bindings/lib/associated_interface_ptr.cc
+++ /dev/null
@@ -1,18 +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 "mojo/public/cpp/bindings/associated_interface_ptr.h"
-
-namespace mojo {
-
-void AssociateWithDisconnectedPipe(ScopedInterfaceEndpointHandle handle) {
-  MessagePipe pipe;
-  scoped_refptr<internal::MultiplexRouter> router =
-      new internal::MultiplexRouter(
-          std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE,
-          false, base::SequencedTaskRunnerHandle::Get());
-  router->AssociateInterface(std::move(handle));
-}
-
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/associated_receiver.cc b/mojo/public/cpp/bindings/lib/associated_receiver.cc
index 060aa85..a1c1d80 100644
--- a/mojo/public/cpp/bindings/lib/associated_receiver.cc
+++ b/mojo/public/cpp/bindings/lib/associated_receiver.cc
@@ -5,7 +5,9 @@
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 
 #include "base/sequenced_task_runner.h"
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
 #include "mojo/public/cpp/bindings/lib/task_runner_helper.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace mojo {
 
@@ -67,4 +69,13 @@
 
 }  // namespace internal
 
+void AssociateWithDisconnectedPipe(ScopedInterfaceEndpointHandle handle) {
+  MessagePipe pipe;
+  scoped_refptr<internal::MultiplexRouter> router =
+      new internal::MultiplexRouter(
+          std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE,
+          false, base::SequencedTaskRunnerHandle::Get());
+  router->AssociateInterface(std::move(handle));
+}
+
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
deleted file mode 100644
index 3d595cc0..0000000
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ /dev/null
@@ -1,22 +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 "mojo/public/cpp/bindings/lib/fixed_buffer.h"
-
-#include <stdlib.h>
-
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-
-namespace mojo {
-namespace internal {
-
-FixedBufferForTesting::FixedBufferForTesting(size_t size)
-    : Buffer(calloc(Align(size), 1), Align(size), 0) {}
-
-FixedBufferForTesting::~FixedBufferForTesting() {
-  free(data());
-}
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
deleted file mode 100644
index 147ce7b..0000000
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ /dev/null
@@ -1,32 +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 MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
-
-#include <cstddef>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-
-namespace mojo {
-namespace internal {
-
-// FixedBufferForTesting owns its buffer. The Leak method may be used to steal
-// the underlying memory.
-class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) FixedBufferForTesting
-    : public Buffer {
- public:
-  explicit FixedBufferForTesting(size_t size);
-  ~FixedBufferForTesting();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FixedBufferForTesting);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 459a4dc..37692a4 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -13,7 +13,6 @@
     "binder_map_unittest.cc",
     "bindings_test_base.cc",
     "bindings_test_base.h",
-    "buffer_unittest.cc",
     "callback_helpers_unittest.cc",
     "connection_group_unittest.cc",
     "connector_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/buffer_unittest.cc b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
deleted file mode 100644
index 2370454b..0000000
--- a/mojo/public/cpp/bindings/tests/buffer_unittest.cc
+++ /dev/null
@@ -1,33 +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 <stddef.h>
-
-#include <limits>
-
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
-#include "mojo/public/cpp/bindings/lib/serialization_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-namespace {
-
-// Tests that FixedBuffer allocates memory aligned to 8 byte boundaries.
-TEST(FixedBufferTest, Alignment) {
-  internal::FixedBufferForTesting buf(internal::Align(10) * 2);
-  ASSERT_EQ(buf.size(), 16u * 2);
-
-  size_t a = buf.Allocate(10);
-  EXPECT_EQ(0u, a);
-
-  size_t b = buf.Allocate(10);
-  ASSERT_EQ(16u, b);
-
-  // Any more allocations would result in an assert, but we can't test that.
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/data_view_unittest.cc b/mojo/public/cpp/bindings/tests/data_view_unittest.cc
index f5628d1..701c5c1e 100644
--- a/mojo/public/cpp/bindings/tests/data_view_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/data_view_unittest.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/test/task_environment.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/mojo/public/cpp/bindings/tests/enum_headers_unittest.cc b/mojo/public/cpp/bindings/tests/enum_headers_unittest.cc
index d69d738..2a71a79 100644
--- a/mojo/public/cpp/bindings/tests/enum_headers_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/enum_headers_unittest.cc
@@ -19,11 +19,6 @@
     for a mojom containing only an enum.
 #endif
 
-#ifdef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
-#error associated_interface_ptr.h should not be included by the generated \
-    header for a mojom containing only an enum.
-#endif
-
 #ifdef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
 #error interface_request.h should not be included by the generated header \
     for a mojom containing only an enum.
diff --git a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
index 75b5841..38c18ca 100644
--- a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
 #include "mojo/public/cpp/system/message_pipe.h"
diff --git a/mojo/public/cpp/bindings/tests/struct_headers_unittest.cc b/mojo/public/cpp/bindings/tests/struct_headers_unittest.cc
index d711870..e15caf3 100644
--- a/mojo/public/cpp/bindings/tests/struct_headers_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_headers_unittest.cc
@@ -19,11 +19,6 @@
     for a mojom containing only a struct.
 #endif
 
-#ifdef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
-#error associated_interface_ptr.h should not be included by the generated \
-    header for a mojom containing only a struct.
-#endif
-
 #ifdef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
 #error interface_request.h should not be included by the generated header \
     for a mojom containing only a struct.
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc
index 7fdec87..04b4ff0 100644
--- a/mojo/public/cpp/bindings/tests/struct_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -7,7 +7,6 @@
 #include <string.h>
 #include <utility>
 
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/interfaces/bindings/tests/test_export2.mojom.h"
 #include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index 6326f71..096bd33 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/validation_context.h"
 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
@@ -287,13 +286,15 @@
 TEST(UnionTest, SerializeIsNullInlined) {
   PodUnionPtr pod;
 
-  Message message;
-  mojo::internal::FixedBufferForTesting buffer(16);
+  Message message(0, 0, 0, 0, nullptr);
+  mojo::internal::Buffer& buffer = *message.payload_buffer();
+  EXPECT_EQ(sizeof(mojo::internal::MessageHeader), buffer.cursor());
+
   internal::PodUnion_Data::BufferWriter writer;
   writer.Allocate(&buffer);
   mojo::internal::Serialize<PodUnionDataView>(pod, &writer, true, &message);
   EXPECT_TRUE(writer.data()->is_null());
-  EXPECT_EQ(16U, buffer.cursor());
+  EXPECT_EQ(16U + sizeof(mojo::internal::MessageHeader), buffer.cursor());
 
   PodUnionPtr pod2;
   mojo::internal::Deserialize<PodUnionDataView>(writer.data(), &pod2, nullptr);
@@ -423,7 +424,8 @@
 
 TEST(UnionTest, NullStringValidation) {
   constexpr size_t size = sizeof(internal::ObjectUnion_Data);
-  mojo::internal::FixedBufferForTesting buffer(size);
+  Message message(0, 0, 0, 0, nullptr);
+  mojo::internal::Buffer& buffer = *message.payload_buffer();
   internal::ObjectUnion_Data::BufferWriter writer;
   writer.Allocate(&buffer);
   writer->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
@@ -436,7 +438,8 @@
 
 TEST(UnionTest, StringPointerOverflowValidation) {
   constexpr size_t size = sizeof(internal::ObjectUnion_Data);
-  mojo::internal::FixedBufferForTesting buffer(size);
+  Message message(0, 0, 0, 0, nullptr);
+  mojo::internal::Buffer& buffer = *message.payload_buffer();
   internal::ObjectUnion_Data::BufferWriter writer;
   writer.Allocate(&buffer);
   writer->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
@@ -448,8 +451,8 @@
 }
 
 TEST(UnionTest, StringValidateOOB) {
-  constexpr size_t size = 32;
-  mojo::internal::FixedBufferForTesting buffer(size);
+  Message message(0, 0, 0, 0, nullptr);
+  mojo::internal::Buffer& buffer = *message.payload_buffer();
   internal::ObjectUnion_Data::BufferWriter writer;
   writer.Allocate(&buffer);
   writer->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
@@ -643,8 +646,8 @@
       SmallStructNonNullableUnion::New());
 
   constexpr size_t size = sizeof(internal::SmallStructNonNullableUnion_Data);
-  mojo::internal::FixedBufferForTesting buffer(size);
-  mojo::Message message;
+  Message message(0, 0, 0, 0, nullptr);
+  mojo::internal::Buffer& buffer = *message.payload_buffer();
   internal::SmallStructNonNullableUnion_Data::BufferWriter writer;
   writer.Allocate(&buffer);
   mojo::internal::ValidationContext validation_context(
diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
index 3f9614e..b439ecf 100644
--- a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
@@ -6,7 +6,6 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/task_environment.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
index 627293e..807845a 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
@@ -180,9 +180,6 @@
 using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
 {{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
 using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
-{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
-using {{interface.name}}AssociatedPtr =
-    mojo::AssociatedInterfacePtr<{{interface.name}}>;
 using {{interface.name}}AssociatedPtrInfo =
     mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
 {{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 0c36f12..567eac0 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -91,7 +91,6 @@
 {%- endif %}
 
 {% if not disallow_interfaces and uses_interfaces -%}
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/associated_interface_request.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
diff --git a/net/base/network_change_notifier_fuchsia_unittest.cc b/net/base/network_change_notifier_fuchsia_unittest.cc
index 138d0192..ca92d80 100644
--- a/net/base/network_change_notifier_fuchsia_unittest.cc
+++ b/net/base/network_change_notifier_fuchsia_unittest.cc
@@ -187,17 +187,17 @@
   ~FakeWatcherAsync() = default;
 
   void Bind(fidl::InterfaceRequest<fuchsia::net::interfaces::Watcher> request) {
-    watcher_.Post(FROM_HERE, &FakeWatcher::Bind, std::move(request));
+    watcher_.AsyncCall(&FakeWatcher::Bind).WithArgs(std::move(request));
   }
 
   // Asynchronously push an event to the watcher.
   void PushEvent(fuchsia::net::interfaces::Event event) {
-    watcher_.Post(FROM_HERE, &FakeWatcher::PushEvent, std::move(event));
+    watcher_.AsyncCall(&FakeWatcher::PushEvent).WithArgs(std::move(event));
   }
 
   // Asynchronously push an initial set of interfaces to the watcher.
   void SetInitial(std::vector<fuchsia::net::interfaces::Properties> props) {
-    watcher_.Post(FROM_HERE, &FakeWatcher::SetInitial, std::move(props));
+    watcher_.AsyncCall(&FakeWatcher::SetInitial).WithArgs(std::move(props));
   }
 
   // Asynchronously push an initial single intface to the watcher.
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 2fe00708..4f286b8 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -307,6 +307,7 @@
     { "name": "cloud.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
     { "name": "code.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
     { "name": "contributor.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
+    { "name": "datastudio.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
     { "name": "dl.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
     { "name": "docs.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
     { "name": "domains.google.com", "policy": "google", "mode": "force-https", "include_subdomains": true, "pins": "google" },
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index 66b90190..3d7d6db 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -48,7 +48,6 @@
       "src/common/platform/api/quiche_string_piece.h",
       "src/common/platform/api/quiche_text_utils.h",
       "src/common/platform/api/quiche_time_utils.h",
-      "src/common/platform/api/quiche_unordered_containers.h",
       "src/common/quiche_data_reader.cc",
       "src/common/quiche_data_reader.h",
       "src/common/quiche_data_writer.cc",
@@ -633,7 +632,6 @@
       "src/spdy/platform/api/spdy_estimate_memory_usage.h",
       "src/spdy/platform/api/spdy_flags.h",
       "src/spdy/platform/api/spdy_logging.h",
-      "src/spdy/platform/api/spdy_macros.h",
       "src/spdy/platform/api/spdy_mem_slice.h",
       "src/spdy/platform/api/spdy_string_utils.h",
     ]
diff --git a/printing/backend/cups_ipp_helper.cc b/printing/backend/cups_ipp_helper.cc
index 570a3a9..6e66d620 100644
--- a/printing/backend/cups_ipp_helper.cc
+++ b/printing/backend/cups_ipp_helper.cc
@@ -336,8 +336,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   printer_info->pin_supported = PinSupported(printer);
-  if (base::FeatureList::IsEnabled(printing::features::kAdvancedPpdAttributes))
-    ExtractAdvancedCapabilities(printer, printer_info);
+  ExtractAdvancedCapabilities(printer, printer_info);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   ExtractCopies(printer, printer_info);
diff --git a/printing/backend/cups_ipp_helper_unittest.cc b/printing/backend/cups_ipp_helper_unittest.cc
index 683f25b..3a1fb8b 100644
--- a/printing/backend/cups_ipp_helper_unittest.cc
+++ b/printing/backend/cups_ipp_helper_unittest.cc
@@ -11,12 +11,9 @@
 
 #include "base/notreached.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "printing/backend/cups_printer.h"
 #include "printing/mojom/print.mojom.h"
-#include "printing/printing_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -327,8 +324,6 @@
 
 TEST_F(PrintBackendCupsIppHelperTest, AdvancedCaps) {
   base::HistogramTester histograms;
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(printing::features::kAdvancedPpdAttributes);
 
   printer_->SetSupportedOptions(
       "job-creation-attributes",
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
index 29be6f8..fcba4f00 100644
--- a/printing/printing_context_chromeos.cc
+++ b/printing/printing_context_chromeos.cc
@@ -200,38 +200,36 @@
     options.push_back(ConstructOption(kIppResolution, dpi + "dpi"));
   }
 
-  if (base::FeatureList::IsEnabled(
-          printing::features::kAdvancedPpdAttributes)) {
-    size_t regular_attr_count = options.size();
-    std::map<std::string, std::vector<std::string>> multival;
-    for (const auto& setting : settings.advanced_settings()) {
-      const std::string& key = setting.first;
-      const std::string& value = setting.second.GetString();
-      if (value.empty())
-        continue;
+  size_t regular_attr_count = options.size();
+  std::map<std::string, std::vector<std::string>> multival;
+  for (const auto& setting : settings.advanced_settings()) {
+    const std::string& key = setting.first;
+    const std::string& value = setting.second.GetString();
+    if (value.empty())
+      continue;
 
-      // Check for multivalue enum ("attribute/value").
-      size_t pos = key.find('/');
-      if (pos == std::string::npos) {
-        // Regular value.
-        ReportEnumUsage(key);
-        options.push_back(ConstructOption(key, value));
-        continue;
-      }
-      // Store selected enum values.
-      if (value == kOptionTrue)
-        multival[key.substr(0, pos)].push_back(key.substr(pos + 1));
+    // Check for multivalue enum ("attribute/value").
+    size_t pos = key.find('/');
+    if (pos == std::string::npos) {
+      // Regular value.
+      ReportEnumUsage(key);
+      options.push_back(ConstructOption(key, value));
+      continue;
     }
-    // Pass multivalue enums as comma-separated lists.
-    for (const auto& it : multival) {
-      ReportEnumUsage(it.first);
-      options.push_back(
-          ConstructOption(it.first, base::JoinString(it.second, ",")));
-    }
-    base::UmaHistogramCounts1000("Printing.CUPS.IppAttributesUsed",
-                                 options.size() - regular_attr_count);
+    // Store selected enum values.
+    if (value == kOptionTrue)
+      multival[key.substr(0, pos)].push_back(key.substr(pos + 1));
   }
 
+  // Pass multivalue enums as comma-separated lists.
+  for (const auto& it : multival) {
+    ReportEnumUsage(it.first);
+    options.push_back(
+        ConstructOption(it.first, base::JoinString(it.second, ",")));
+  }
+  base::UmaHistogramCounts1000("Printing.CUPS.IppAttributesUsed",
+                               options.size() - regular_attr_count);
+
   return options;
 }
 
diff --git a/printing/printing_features.cc b/printing/printing_features.cc
index 637f7ff4..237a9d9 100644
--- a/printing/printing_features.cc
+++ b/printing/printing_features.cc
@@ -9,12 +9,6 @@
 namespace printing {
 namespace features {
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// Enables Advanced PPD Attributes.
-const base::Feature kAdvancedPpdAttributes{"AdvancedPpdAttributes",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
 #if defined(OS_MAC)
 // Use the CUPS IPP printing backend instead of the original CUPS backend that
 // calls the deprecated PPD API.
diff --git a/printing/printing_features.h b/printing/printing_features.h
index 35faf4a..72b155a 100644
--- a/printing/printing_features.h
+++ b/printing/printing_features.h
@@ -16,10 +16,6 @@
 // The following features are declared alphabetically. The features should be
 // documented with descriptions of their behaviors in the .cc file.
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-PRINTING_EXPORT extern const base::Feature kAdvancedPpdAttributes;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
 #if defined(OS_MAC)
 PRINTING_EXPORT extern const base::Feature kCupsIppPrintingBackend;
 #endif  // defined(OS_MAC)
diff --git a/remoting/base/task_util.h b/remoting/base/task_util.h
index c876fe4..a95e5fa2 100644
--- a/remoting/base/task_util.h
+++ b/remoting/base/task_util.h
@@ -37,7 +37,8 @@
 // Say if you want to call this method and make |callback| run on the current
 // sequence:
 //
-//   client_.Post(FROM_HERE, &DirectoryClient::DeleteHost, host_id, callback);
+//   client_.AsyncCall(&DirectoryClient::DeleteHost).WithArgs(host_id,
+//   callback);
 //
 // You can just do:
 //
@@ -59,8 +60,9 @@
                       void (SequenceBoundType::*method)(MethodArgs...),
                       base::OnceCallback<void(CallbackArgs...)> callback,
                       Args&&... args) {
-  client->Post(from_here, method, std::forward<Args>(args)...,
-               WrapCallbackToCurrentSequence(from_here, std::move(callback)));
+  client->AsyncCall(method, from_here)
+      .WithArgs(std::forward<Args>(args)...,
+                WrapCallbackToCurrentSequence(from_here, std::move(callback)));
 }
 
 }  // namespace remoting
diff --git a/remoting/host/file_transfer/file_chooser_mac.mm b/remoting/host/file_transfer/file_chooser_mac.mm
index 45f7389e..71d8af4 100644
--- a/remoting/host/file_transfer/file_chooser_mac.mm
+++ b/remoting/host/file_transfer/file_chooser_mac.mm
@@ -146,8 +146,7 @@
 }
 
 void FileChooserMac::Show() {
-  mac_file_chooser_on_ui_thread_.Post(FROM_HERE,
-                                      &MacFileChooserOnUiThread::Show);
+  mac_file_chooser_on_ui_thread_.AsyncCall(&MacFileChooserOnUiThread::Show);
 }
 
 void FileChooserMac::RunCallback(FileChooser::Result result) {
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 03fa3de..ce9bca0 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1590,11 +1590,6 @@
 
   host_->AddExtension(std::make_unique<TestEchoExtension>());
 
-  // TODO(joedow): Remove in M90.
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-  host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
-#endif
-
   if (max_session_duration_minutes_ > 0) {
     host_->SetMaximumSessionDuration(
         base::TimeDelta::FromMinutes(max_session_duration_minutes_));
diff --git a/remoting/ios/facade/host_list_service.mm b/remoting/ios/facade/host_list_service.mm
index a728840a..13c87f6 100644
--- a/remoting/ios/facade/host_list_service.mm
+++ b/remoting/ios/facade/host_list_service.mm
@@ -177,8 +177,7 @@
 }
 
 void HostListService::OnUserUpdated(bool is_user_signed_in) {
-  directory_client_.Post(FROM_HERE,
-                         &DirectoryServiceClient::CancelPendingRequests);
+  directory_client_.AsyncCall(&DirectoryServiceClient::CancelPendingRequests);
   SetState(State::NOT_FETCHED);
   if (is_user_signed_in) {
     RequestFetch();
diff --git a/sandbox/policy/mac/gpu_v2.sb b/sandbox/policy/mac/gpu_v2.sb
index 1bb5edc..a01030e 100644
--- a/sandbox/policy/mac/gpu_v2.sb
+++ b/sandbox/policy/mac/gpu_v2.sb
@@ -14,6 +14,8 @@
 
 (allow ipc-posix-shm)
 
+(define disable-metal-shader-cache "DISABLE_METAL_SHADER_CACHE")
+
 ; TODO(https://crbug.com/1126350): Remove this after debugging. These blocks
 ; enumerate known denials, while turning unknown denials into fatal crashes.
 (define crash-on-unknown-denials #f) ; Single-line kill switch.
@@ -171,3 +173,11 @@
       (syscall-number SYS_write)
       (syscall-number SYS_write_nocancel)
 )))
+
+; crbug.com/1159113
+(if (param-true? disable-metal-shader-cache)
+  (let ((metal-cache-dir (subpath (string-append (param darwin-user-cache-dir)
+                                                 "/com.apple.metal"))))
+    (deny file-read* metal-cache-dir)
+    (deny file-write* metal-cache-dir))
+)
diff --git a/sandbox/policy/mac/sandbox_mac.h b/sandbox/policy/mac/sandbox_mac.h
index 6d10a37..7740d30 100644
--- a/sandbox/policy/mac/sandbox_mac.h
+++ b/sandbox/policy/mac/sandbox_mac.h
@@ -60,6 +60,7 @@
   static const char* kSandboxFieldTrialSeverName;
 
   static const char* kSandboxBundleVersionPath;
+  static const char* kSandboxDisableMetalShaderCache;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(MacDirAccessSandboxTest, StringEscape);
diff --git a/sandbox/policy/mac/sandbox_mac.mm b/sandbox/policy/mac/sandbox_mac.mm
index 9d2de6d6..179675d 100644
--- a/sandbox/policy/mac/sandbox_mac.mm
+++ b/sandbox/policy/mac/sandbox_mac.mm
@@ -73,6 +73,8 @@
 const char* SandboxMac::kSandboxMacOS1013 = "MACOS_1013";
 const char* SandboxMac::kSandboxFieldTrialSeverName = "FIELD_TRIAL_SERVER_NAME";
 const char* SandboxMac::kSandboxBundleVersionPath = "BUNDLE_VERSION_PATH";
+const char* SandboxMac::kSandboxDisableMetalShaderCache =
+    "DISABLE_METAL_SHADER_CACHE";
 
 // Warm up System APIs that empirically need to be accessed before the Sandbox
 // is turned on.
diff --git a/sandbox/policy/switches.cc b/sandbox/policy/switches.cc
index 7530bef..5abf7ed 100644
--- a/sandbox/policy/switches.cc
+++ b/sandbox/policy/switches.cc
@@ -108,6 +108,9 @@
 // Cause the OS X sandbox write to syslog every time an access to a resource
 // is denied by the sandbox.
 const char kEnableSandboxLogging[] = "enable-sandbox-logging";
+
+// Disables Metal's shader cache, using the GPU sandbox to prevent access to it.
+const char kDisableMetalShaderCache[] = "disable-metal-shader-cache";
 #endif
 
 // Flags spied upon from other layers.
diff --git a/sandbox/policy/switches.h b/sandbox/policy/switches.h
index f5d88277..3db13f3 100644
--- a/sandbox/policy/switches.h
+++ b/sandbox/policy/switches.h
@@ -65,6 +65,7 @@
 #endif
 #if defined(OS_MAC)
 SANDBOX_POLICY_EXPORT extern const char kEnableSandboxLogging[];
+SANDBOX_POLICY_EXPORT extern const char kDisableMetalShaderCache[];
 #endif
 
 // Flags spied upon from other layers.
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 8cecfab..998b1a68 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -745,6 +745,41 @@
   return SBOX_ALL_OK;
 }
 
+// Launches outside of the sandbox - the process will not be associated with
+// a Policy or TargetProcess. This supports both kNoSandbox and the --no-sandbox
+// command line flag.
+ResultCode LaunchWithoutSandbox(
+    base::CommandLine* cmd_line,
+    const base::HandlesToInheritVector& handles_to_inherit,
+    SandboxDelegate* delegate,
+    base::Process* process) {
+  base::LaunchOptions options;
+  options.handles_to_inherit = handles_to_inherit;
+  // Network process runs in a job even when unsandboxed. This is to ensure it
+  // does not outlive the browser, which could happen if there is a lot of I/O
+  // on process shutdown, in which case TerminateProcess can fail. See
+  // https://crbug.com/820996.
+  if (delegate->ShouldUnsandboxedRunInJob()) {
+    BOOL in_job = true;
+    // Prior to Windows 8 nested jobs aren't possible.
+    if (base::win::GetVersion() >= base::win::Version::WIN8 ||
+        (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) &&
+         !in_job)) {
+      static HANDLE job_object_handle = nullptr;
+      if (!job_object_handle) {
+        Job job_obj;
+        DWORD result = job_obj.Init(JOB_UNPROTECTED, nullptr, 0, 0);
+        if (result != ERROR_SUCCESS)
+          return SBOX_ERROR_CANNOT_INIT_JOB;
+        job_object_handle = job_obj.Take().Take();
+      }
+      options.job_handle = job_object_handle;
+    }
+  }
+  *process = base::LaunchProcess(*cmd_line, options);
+  return SBOX_ALL_OK;
+}
+
 }  // namespace
 
 // static
@@ -921,34 +956,12 @@
   }
 
   SandboxType sandbox_type = delegate->GetSandboxType();
+  // --no-sandbox and kNoSandbox are launched without creating a Policy.
   if (IsUnsandboxedSandboxType(sandbox_type) ||
       cmd_line->HasSwitch(switches::kNoSandbox) ||
       launcher_process_command_line.HasSwitch(switches::kNoSandbox)) {
-    base::LaunchOptions options;
-    options.handles_to_inherit = handles_to_inherit;
-    // Network process runs in a job even when unsandboxed. This is to ensure it
-    // does not outlive the browser, which could happen if there is a lot of I/O
-    // on process shutdown, in which case TerminateProcess can fail. See
-    // https://crbug.com/820996.
-    if (delegate->ShouldUnsandboxedRunInJob()) {
-      BOOL in_job = true;
-      // Prior to Windows 8 nested jobs aren't possible.
-      if (base::win::GetVersion() >= base::win::Version::WIN8 ||
-          (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) &&
-           !in_job)) {
-        static HANDLE job_object_handle = nullptr;
-        if (!job_object_handle) {
-          Job job_obj;
-          DWORD result = job_obj.Init(JOB_UNPROTECTED, nullptr, 0, 0);
-          if (result != ERROR_SUCCESS)
-            return SBOX_ERROR_CANNOT_INIT_JOB;
-          job_object_handle = job_obj.Take().Take();
-        }
-        options.job_handle = job_object_handle;
-      }
-    }
-    *process = base::LaunchProcess(*cmd_line, options);
-    return SBOX_ALL_OK;
+    return LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate,
+                                process);
   }
 
   scoped_refptr<TargetPolicy> policy = g_broker_services->CreatePolicy();
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index 1edf8b62..1515a8af 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -627,6 +627,7 @@
     base::debug::ScopedCrashKeyString scoped_key_string_url(
         url_origin, url::Origin::Create(url).GetDebugString());
 
+    NOTREACHED();
     base::debug::DumpWithoutCrashing();
     return false;
   }
diff --git a/services/network/web_bundle_chunked_buffer.cc b/services/network/web_bundle_chunked_buffer.cc
index 2c3a5e2..8d5a84b 100644
--- a/services/network/web_bundle_chunked_buffer.cc
+++ b/services/network/web_bundle_chunked_buffer.cc
@@ -121,6 +121,11 @@
       CreatePartialBuffer(offset, length), offset, length);
 }
 
+uint64_t WebBundleChunkedBuffer::size() const {
+  DCHECK_GE(end_pos(), start_pos());
+  return end_pos() - start_pos();
+}
+
 WebBundleChunkedBuffer::ChunkVector::const_iterator
 WebBundleChunkedBuffer::FindChunk(uint64_t pos) const {
   if (empty())
diff --git a/services/network/web_bundle_chunked_buffer.h b/services/network/web_bundle_chunked_buffer.h
index 3c297a8..b28471f0 100644
--- a/services/network/web_bundle_chunked_buffer.h
+++ b/services/network/web_bundle_chunked_buffer.h
@@ -47,6 +47,9 @@
       uint64_t offset,
       uint64_t max_length) const;
 
+  // Returns the buffer size.
+  uint64_t size() const;
+
  private:
   friend class WebBundleChunkedBufferTest;
   FRIEND_TEST_ALL_PREFIXES(WebBundleChunkedBufferTest, EmptyBuffer);
diff --git a/services/network/web_bundle_manager.cc b/services/network/web_bundle_manager.cc
index 017b3e9..db1abe9 100644
--- a/services/network/web_bundle_manager.cc
+++ b/services/network/web_bundle_manager.cc
@@ -177,6 +177,12 @@
     return false;
   }
   memory_usage_per_process_[process_id] += num_bytes;
+
+  if (max_memory_usage_per_process_[process_id] <
+      memory_usage_per_process_[process_id]) {
+    max_memory_usage_per_process_[process_id] =
+        memory_usage_per_process_[process_id];
+  }
   return true;
 }
 
@@ -187,6 +193,10 @@
   memory_usage_per_process_[process_id] -= num_bytes;
   if (memory_usage_per_process_[process_id] == 0) {
     memory_usage_per_process_.erase(process_id);
+    base::UmaHistogramCustomCounts(
+        "SubresourceWebBundles.MaxMemoryUsagePerProcess",
+        max_memory_usage_per_process_[process_id], 1, 50000000, 50);
+    max_memory_usage_per_process_.erase(process_id);
   }
 }
 
diff --git a/services/network/web_bundle_manager.h b/services/network/web_bundle_manager.h
index f92d9e6..7c843df 100644
--- a/services/network/web_bundle_manager.h
+++ b/services/network/web_bundle_manager.h
@@ -73,6 +73,7 @@
 
   uint64_t max_memory_per_process_;
   std::map<int32_t, uint64_t> memory_usage_per_process_;
+  std::map<int32_t, uint64_t> max_memory_usage_per_process_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/services/network/web_bundle_manager_unittest.cc b/services/network/web_bundle_manager_unittest.cc
index 137546a..1ffaba6 100644
--- a/services/network/web_bundle_manager_unittest.cc
+++ b/services/network/web_bundle_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/web_bundle_manager.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/unguessable_token.h"
 #include "components/web_package/test_support/web_bundle_builder.h"
@@ -297,6 +298,7 @@
 }
 
 TEST_F(WebBundleManagerTest, MemoryQuota_StartRequestAfterError) {
+  base::HistogramTester histogram_tester;
   WebBundleManager manager;
 
   std::string bundle = CreateSmallBundleString();
@@ -317,6 +319,11 @@
   EXPECT_EQ(handle->last_bundle_error()->first,
             mojom::WebBundleErrorType::kMemoryQuotaExceeded);
   EXPECT_EQ(handle->last_bundle_error()->second, kQuotaExceededErrorMessage);
+  histogram_tester.ExpectUniqueSample(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
+          kMemoryQuotaExceeded,
+      1);
 
   // Start the subresource request after triggering the quota error.
   mojo::Remote<network::mojom::URLLoader> loader;
@@ -479,13 +486,14 @@
 }
 
 TEST_F(WebBundleManagerTest, MemoryQuota_ProcessIsolation) {
+  base::HistogramTester histogram_tester;
   WebBundleManager manager;
 
   std::string bundle = CreateSmallBundleString();
 
-  // Set the max memory to trigger the quota error while loading the second
+  // Set the max memory to trigger the quota error while loading the third
   // web bundle.
-  SetMaxMemoryPerProces(manager, bundle.size() * 1.5);
+  SetMaxMemoryPerProces(manager, bundle.size() * 2.5);
 
   // Start loading the first web bundle in the process 1.
   base::WeakPtr<WebBundleURLLoaderFactory> factory1_1;
@@ -508,6 +516,11 @@
   EXPECT_TRUE(
       mojo::BlockingCopyToString(client1_1->response_body_release(), &body1_1));
   EXPECT_EQ("body", body1_1);
+  histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
+                                      bundle.size(), 1);
+  histogram_tester.ExpectUniqueSample(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 1);
 
   // Start loading the second web bundle in the process 1.
   base::WeakPtr<WebBundleURLLoaderFactory> factory1_2;
@@ -517,21 +530,53 @@
   auto producer1_2 = SetBundleStream(*factory1_2);
   mojo::BlockingCopyFromString(bundle, producer1_2);
   producer1_2.reset();
-  // TestWebBundleHandle must receive the error.
-  handle1_2->RunUntilBundleError();
-  ASSERT_TRUE(handle1_2->last_bundle_error().has_value());
-  EXPECT_EQ(handle1_2->last_bundle_error()->first,
-            mojom::WebBundleErrorType::kMemoryQuotaExceeded);
-  EXPECT_EQ(handle1_2->last_bundle_error()->second, kQuotaExceededErrorMessage);
 
   // Start loading the subresource from the second web bundle.
   mojo::Remote<network::mojom::URLLoader> loader1_2;
   std::unique_ptr<network::TestURLLoaderClient> client1_2;
   std::tie(loader1_2, client1_2) = StartSubresourceLoad(*factory1_2);
-  // The subresource request must fail.
+  // Confirm that the subresource is correctly loaded.
   client1_2->RunUntilComplete();
+  EXPECT_EQ(net::OK, client1_2->completion_status().error_code);
+  EXPECT_EQ(client1_2->response_head()->web_bundle_url, GURL(kBundleUrl));
+  std::string body1_2;
+  EXPECT_TRUE(
+      mojo::BlockingCopyToString(client1_2->response_body_release(), &body1_2));
+  EXPECT_EQ("body", body1_2);
+  histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
+                                      bundle.size(), 2);
+  histogram_tester.ExpectUniqueSample(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 2);
+
+  // Start loading the third web bundle in the process 1.
+  base::WeakPtr<WebBundleURLLoaderFactory> factory1_3;
+  std::unique_ptr<TestWebBundleHandle> handle1_3;
+  std::tie(factory1_3, handle1_3) =
+      CreateWebBundleLoaderFactory(manager, process_id1);
+  auto producer1_3 = SetBundleStream(*factory1_3);
+  mojo::BlockingCopyFromString(bundle, producer1_3);
+  producer1_3.reset();
+  // TestWebBundleHandle must receive the error.
+  handle1_3->RunUntilBundleError();
+  ASSERT_TRUE(handle1_3->last_bundle_error().has_value());
+  EXPECT_EQ(handle1_3->last_bundle_error()->first,
+            mojom::WebBundleErrorType::kMemoryQuotaExceeded);
+  EXPECT_EQ(handle1_3->last_bundle_error()->second, kQuotaExceededErrorMessage);
+
+  // Start loading the subresource from the second web bundle.
+  mojo::Remote<network::mojom::URLLoader> loader1_3;
+  std::unique_ptr<network::TestURLLoaderClient> client1_3;
+  std::tie(loader1_3, client1_3) = StartSubresourceLoad(*factory1_3);
+  // The subresource request must fail.
+  client1_3->RunUntilComplete();
   EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
-            client1_2->completion_status().error_code);
+            client1_3->completion_status().error_code);
+  histogram_tester.ExpectBucketCount(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
+          kMemoryQuotaExceeded,
+      1);
 
   // Start loading the third web bundle in the process 2.
   base::WeakPtr<WebBundleURLLoaderFactory> factory2;
@@ -553,6 +598,23 @@
   EXPECT_TRUE(
       mojo::BlockingCopyToString(client2->response_body_release(), &body2));
   EXPECT_EQ("body", body2);
+  histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
+                                      bundle.size(), 3);
+  histogram_tester.ExpectBucketCount(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 3);
+
+  // Reset handles and RunUntilIdle to trigger MaxMemoryUsagePerProcess
+  // histogram count.
+  handle1_1.reset();
+  handle1_2.reset();
+  handle1_3.reset();
+  handle2.reset();
+  base::RunLoop().RunUntilIdle();
+  histogram_tester.ExpectBucketCount(
+      "SubresourceWebBundles.MaxMemoryUsagePerProcess", bundle.size() * 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "SubresourceWebBundles.MaxMemoryUsagePerProcess", bundle.size(), 1);
 }
 
 }  // namespace network
diff --git a/services/network/web_bundle_url_loader_factory.cc b/services/network/web_bundle_url_loader_factory.cc
index 2c38387..d5d12ca 100644
--- a/services/network/web_bundle_url_loader_factory.cc
+++ b/services/network/web_bundle_url_loader_factory.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/web_bundle_url_loader_factory.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/web_package/web_bundle_parser.h"
@@ -23,6 +24,11 @@
 
 constexpr size_t kBlockedBodyAllocationSize = 1;
 
+void RecordLoadResult(
+    WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult result) {
+  base::UmaHistogramEnumeration("SubresourceWebBundles.LoadResult", result);
+}
+
 void DeleteProducerAndRunCallback(
     std::unique_ptr<mojo::DataPipeProducer> producer,
     base::OnceCallback<void(MojoResult result)> callback,
@@ -42,6 +48,10 @@
   // network::mojom::URLLoaderClient implementation:
   void OnReceiveResponse(
       network::mojom::URLResponseHeadPtr response_head) override {
+    base::UmaHistogramCustomCounts(
+        "SubresourceWebBundles.ContentLength",
+        response_head->content_length < 0 ? 0 : response_head->content_length,
+        1, 50000000, 50);
     wrapped_->OnReceiveResponse(std::move(response_head));
   }
 
@@ -234,7 +244,8 @@
                    mojo::ScopedDataPipeConsumerHandle bundle_body,
                    std::unique_ptr<WebBundleMemoryQuotaConsumer>
                        web_bundle_memory_quota_consumer,
-                   base::OnceClosure memory_quota_exceeded_closure)
+                   base::OnceClosure memory_quota_exceeded_closure,
+                   base::OnceClosure data_completed_closure)
       : data_source_receiver_(this, std::move(data_source_receiver)),
         pipe_drainer_(
             std::make_unique<mojo::DataPipeDrainer>(this,
@@ -242,7 +253,8 @@
         web_bundle_memory_quota_consumer_(
             std::move(web_bundle_memory_quota_consumer)),
         memory_quota_exceeded_closure_(
-            std::move(memory_quota_exceeded_closure)) {}
+            std::move(memory_quota_exceeded_closure)),
+        data_completed_closure_(std::move(data_completed_closure)) {}
 
   ~BundleDataSource() override {
     // The receiver must be closed before destructing pending callbacks in
@@ -321,6 +333,15 @@
 
   void OnDataComplete() override {
     DCHECK(!finished_loading_);
+    base::UmaHistogramCustomCounts(
+        "SubresourceWebBundles.ReceivedSize",
+        base::saturated_cast<base::Histogram::Sample>(buffer_.size()), 1,
+        50000000, 50);
+    DCHECK(data_completed_closure_);
+    // Defer calling |data_completed_closure_| not to run
+    // |data_completed_closure_| before |memory_quota_exceeded_closure_|.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, std::move(data_completed_closure_));
     finished_loading_ = true;
     ProcessPendingReads();
   }
@@ -375,6 +396,7 @@
   std::unique_ptr<WebBundleMemoryQuotaConsumer>
       web_bundle_memory_quota_consumer_;
   base::OnceClosure memory_quota_exceeded_closure_;
+  base::OnceClosure data_completed_closure_;
 };
 
 WebBundleURLLoaderFactory::WebBundleURLLoaderFactory(
@@ -408,6 +430,8 @@
       data_source.InitWithNewPipeAndPassReceiver(), std::move(body),
       std::move(web_bundle_memory_quota_consumer_),
       base::BindOnce(&WebBundleURLLoaderFactory::OnMemoryQuotaExceeded,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&WebBundleURLLoaderFactory::OnDataCompleted,
                      weak_ptr_factory_.GetWeakPtr()));
   // WebBundleParser will self-destruct on remote mojo ends' disconnection.
   new web_package::WebBundleParser(parser_.BindNewPipeAndPassReceiver(),
@@ -481,6 +505,7 @@
   TRACE_EVENT0("loading", "WebBundleURLLoaderFactory::OnMetadataParsed");
   if (error) {
     metadata_error_ = std::move(error);
+    MaybeRecordLoadResult();
     web_bundle_handle_->OnWebBundleError(
         mojom::WebBundleErrorType::kMetadataParseError,
         metadata_error_->message);
@@ -496,6 +521,7 @@
   }
 
   metadata_ = std::move(metadata);
+  MaybeRecordLoadResult();
   for (auto loader : pending_loaders_)
     StartLoad(loader.get());
   pending_loaders_.clear();
@@ -561,6 +587,7 @@
 void WebBundleURLLoaderFactory::OnMemoryQuotaExceeded() {
   TRACE_EVENT0("loading", "WebBundleURLLoaderFactory::OnMemoryQuotaExceeded");
   quota_exceeded_error_ = true;
+  MaybeRecordLoadResult();
   web_bundle_handle_->OnWebBundleError(
       mojom::WebBundleErrorType::kMemoryQuotaExceeded,
       "Memory quota exceeded. Currently, there is an upper limit on the total "
@@ -576,4 +603,29 @@
   parser_.reset();
 }
 
+void WebBundleURLLoaderFactory::OnDataCompleted() {
+  data_completed_ = true;
+  MaybeRecordLoadResult();
+}
+
+void WebBundleURLLoaderFactory::MaybeRecordLoadResult() {
+  if (load_result_recorded_)
+    return;
+  if (quota_exceeded_error_) {
+    RecordLoadResult(SubresourceWebBundleLoadResult::kMemoryQuotaExceeded);
+    load_result_recorded_ = true;
+    return;
+  }
+  if (metadata_error_) {
+    RecordLoadResult(SubresourceWebBundleLoadResult::kMetadataParseError);
+    load_result_recorded_ = true;
+    return;
+  }
+  if (metadata_ && data_completed_) {
+    RecordLoadResult(SubresourceWebBundleLoadResult::kSuccess);
+    load_result_recorded_ = true;
+    return;
+  }
+}
+
 }  // namespace network
diff --git a/services/network/web_bundle_url_loader_factory.h b/services/network/web_bundle_url_loader_factory.h
index c1d0375..a5e5e85 100644
--- a/services/network/web_bundle_url_loader_factory.h
+++ b/services/network/web_bundle_url_loader_factory.h
@@ -19,6 +19,14 @@
 
 class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleURLLoaderFactory {
  public:
+  // Used for UMA. Append-only.
+  enum class SubresourceWebBundleLoadResult {
+    kSuccess = 0,
+    kMetadataParseError = 1,
+    kMemoryQuotaExceeded = 2,
+    kMaxValue = kMemoryQuotaExceeded,
+  };
+
   WebBundleURLLoaderFactory(
       const GURL& bundle_url,
       mojo::Remote<mojom::WebBundleHandle> web_bundle_handle,
@@ -52,6 +60,8 @@
                         web_package::mojom::BundleResponsePtr response,
                         web_package::mojom::BundleResponseParseErrorPtr error);
   void OnMemoryQuotaExceeded();
+  void OnDataCompleted();
+  void MaybeRecordLoadResult();
 
   GURL bundle_url_;
   mojo::Remote<mojom::WebBundleHandle> web_bundle_handle_;
@@ -63,6 +73,8 @@
   web_package::mojom::BundleMetadataPtr metadata_;
   web_package::mojom::BundleMetadataParseErrorPtr metadata_error_;
   bool quota_exceeded_error_ = false;
+  bool data_completed_ = false;
+  bool load_result_recorded_ = false;
   std::vector<base::WeakPtr<URLLoader>> pending_loaders_;
 
   base::WeakPtrFactory<WebBundleURLLoaderFactory> weak_ptr_factory_{this};
diff --git a/services/network/web_bundle_url_loader_factory_unittest.cc b/services/network/web_bundle_url_loader_factory_unittest.cc
index 58270e9..0afde55 100644
--- a/services/network/web_bundle_url_loader_factory_unittest.cc
+++ b/services/network/web_bundle_url_loader_factory_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/web_bundle_url_loader_factory.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/web_package/test_support/web_bundle_builder.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -174,6 +175,7 @@
 };
 
 TEST_F(WebBundleURLLoaderFactoryTest, Basic) {
+  base::HistogramTester histogram_tester;
   WriteBundle(CreateSmallBundle());
   FinishWritingBundle();
 
@@ -187,9 +189,13 @@
   EXPECT_TRUE(mojo::BlockingCopyToString(
       request.client->response_body_release(), &body));
   EXPECT_EQ("body", body);
+  histogram_tester.ExpectUniqueSample(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 1);
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, MetadataParseError) {
+  base::HistogramTester histogram_tester;
   auto request = StartRequest(GURL(kResourceUrl));
 
   std::vector<uint8_t> bundle = CreateSmallBundle();
@@ -212,6 +218,11 @@
 
   EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
             request2.client->completion_status().error_code);
+  histogram_tester.ExpectUniqueSample(
+      "SubresourceWebBundles.LoadResult",
+      WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
+          kMetadataParseError,
+      1);
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, ResponseParseError) {
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index a0e34a57..55e4161 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1,31 +1,11 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "chromeos-arm-generic-beta": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ]
-  },
   "chromeos-arm-generic-cfi-thin-lto-chrome": {
     "additional_compile_targets": [
       "chromiumos_preflight"
     ]
   },
-  "chromeos-arm-generic-ltc": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ]
-  },
-  "chromeos-arm-generic-lts": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ]
-  },
-  "chromeos-arm-generic-stable": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ]
-  },
   "chromeos-betty-pi-arc-cfi-thin-lto-chrome": {
     "additional_compile_targets": [
       "chromiumos_preflight"
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 80da521..9a446fe 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -75,6 +75,13 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "PQ3A.190801.002",
@@ -106,6 +113,13 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "PQ3A.190801.002",
@@ -137,6 +151,13 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "PQ3A.190801.002",
@@ -178,11 +199,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -192,7 +213,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -257,11 +278,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -271,7 +292,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -415,11 +436,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -429,7 +450,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -494,11 +515,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -508,7 +529,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -656,11 +677,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -670,7 +691,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -735,11 +756,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -749,7 +770,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -893,11 +914,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -907,7 +928,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -972,11 +993,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -986,7 +1007,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1197,11 +1218,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -1211,7 +1232,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1276,11 +1297,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -1290,7 +1311,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1434,11 +1455,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -1448,7 +1469,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1513,11 +1534,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -1527,7 +1548,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1738,11 +1759,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -1752,7 +1773,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1817,11 +1838,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -1831,7 +1852,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1975,11 +1996,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.175",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.176",
         "resultdb": {
           "enable": true
         },
@@ -1989,7 +2010,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.175"
+              "revision": "version:88.0.4324.176"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2054,11 +2075,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.46",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.48",
         "resultdb": {
           "enable": true
         },
@@ -2068,7 +2089,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.46"
+              "revision": "version:89.0.4389.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2213,6 +2234,13 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index e1e0242..f74c362 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -2233,6 +2233,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "LMY48M|LMY48I",
@@ -5887,6 +5894,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "LMY49B",
@@ -9537,6 +9551,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -13174,6 +13195,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MRA58Z",
@@ -18090,6 +18118,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -21521,6 +21556,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
@@ -22419,6 +22461,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86",
@@ -24703,6 +24752,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "LMY48M",
@@ -27692,6 +27748,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -30792,6 +30855,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -30820,6 +30890,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -30852,6 +30929,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -31740,6 +31824,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
@@ -35407,6 +35498,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
@@ -35446,6 +35544,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
@@ -35689,6 +35794,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -39326,6 +39438,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -39353,6 +39472,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -39377,6 +39503,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -39408,6 +39541,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -39439,6 +39579,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "NJH47F",
@@ -41126,6 +41273,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
diff --git a/testing/buildbot/chromium.angle.json b/testing/buildbot/chromium.angle.json
index b3d718e..2df0831 100644
--- a/testing/buildbot/chromium.angle.json
+++ b/testing/buildbot/chromium.angle.json
@@ -20,6 +20,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -52,6 +59,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -84,6 +98,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -116,6 +137,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -146,6 +174,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -186,6 +221,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -227,6 +269,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -266,6 +315,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -305,6 +361,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -338,6 +401,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -371,6 +441,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -404,6 +481,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -437,6 +521,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -469,6 +560,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -502,6 +600,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -534,6 +639,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -567,6 +679,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -598,6 +717,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -639,6 +765,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -677,6 +810,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -715,6 +855,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -746,6 +893,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -787,6 +941,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -825,6 +986,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index a3e7e96..23f8642 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -5337,6 +5337,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "LMY48M",
@@ -8323,6 +8330,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "cpu": "x86-64",
@@ -8820,6 +8834,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -23882,6 +23903,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -26354,6 +26378,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index fa90c5b..4bf15b9 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1636,6 +1636,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -3607,6 +3610,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -7595,6 +7605,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "PQ3A.190801.002",
@@ -46558,6 +46575,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -48641,6 +48661,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -53818,6 +53841,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -57199,6 +57225,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index e8d01d5..ba42340 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -16,6 +16,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -43,6 +50,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -80,6 +94,13 @@
         "name": "angle_perftests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -116,6 +137,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2621,6 +2649,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2652,6 +2687,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2693,6 +2735,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2731,6 +2780,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2765,6 +2821,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2798,6 +2861,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2831,6 +2901,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2864,6 +2941,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2897,6 +2981,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2929,6 +3020,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2962,6 +3060,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -2994,6 +3099,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3034,6 +3146,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3072,6 +3191,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3103,6 +3229,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3144,6 +3277,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3182,6 +3322,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3216,6 +3363,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3249,6 +3403,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3282,6 +3443,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3315,6 +3483,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3348,6 +3523,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3380,6 +3562,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3413,6 +3602,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3445,6 +3641,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3480,6 +3683,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3659,6 +3869,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3695,6 +3912,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3731,6 +3955,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3767,6 +3998,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3807,6 +4045,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3852,6 +4097,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3897,6 +4149,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3934,6 +4193,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -3970,6 +4236,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4007,6 +4280,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4041,6 +4321,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4220,6 +4507,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4256,6 +4550,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4292,6 +4593,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4328,6 +4636,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4368,6 +4683,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4413,6 +4735,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4458,6 +4787,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4495,6 +4831,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4531,6 +4874,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4568,6 +4918,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4604,6 +4961,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4697,6 +5061,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4734,6 +5105,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4769,6 +5147,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4804,6 +5189,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4839,6 +5231,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4874,6 +5273,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4913,6 +5319,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -4957,6 +5370,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5001,6 +5421,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5032,6 +5459,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5067,6 +5501,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5102,6 +5543,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5138,6 +5586,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5175,6 +5630,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5208,6 +5670,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5387,6 +5856,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5423,6 +5899,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5459,6 +5942,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5495,6 +5985,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5535,6 +6032,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5580,6 +6084,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5625,6 +6136,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5662,6 +6180,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5698,6 +6223,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5735,6 +6267,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5769,6 +6308,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5950,6 +6496,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -5986,6 +6539,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6022,6 +6582,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6058,6 +6625,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6094,6 +6668,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6134,6 +6715,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6179,6 +6767,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6224,6 +6819,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6261,6 +6863,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6297,6 +6906,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6334,6 +6950,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6384,6 +7007,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6421,6 +7051,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6459,6 +7096,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6498,6 +7142,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6539,6 +7190,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6588,6 +7246,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6690,6 +7355,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6722,6 +7394,13 @@
         "name": "angle_deqp_egl_gles_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6751,6 +7430,13 @@
         "name": "angle_deqp_gles2_gles_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -6780,6 +7466,13 @@
         "name": "angle_deqp_gles3_gles_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 36b2d951..ed2e670e 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -24,6 +24,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -59,6 +66,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -94,6 +108,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -129,6 +150,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -168,6 +196,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -212,6 +247,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -256,6 +298,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -285,6 +334,13 @@
         "name": "rendering_representative_perf_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -320,6 +376,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -355,6 +418,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
@@ -391,6 +461,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "containment_type": "AUTO",
           "dimension_sets": [
             {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 311271fc..9dd69c3 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -7768,6 +7768,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -11784,6 +11787,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -14049,6 +14055,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index affcbdd5..a3438e9 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -1433,6 +1433,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -3152,6 +3155,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -4945,6 +4951,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -8272,6 +8281,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -9984,6 +9996,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 871b1714..87b63bd 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -199,6 +199,13 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 2de5c975..deaa736 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -239,7 +239,9 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=custom",
+          "--custom-device-target=internal.astro_target"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -253,6 +255,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "cpu": null,
               "device_type": "Astro",
               "os": "Fuchsia",
               "pool": "chrome.tests"
@@ -285,13 +288,13 @@
           "--test-shard-map-filename=lacros-eve-perf-fyi_map.json",
           "--remote=variable_chromeos_device_hostname"
         ],
-        "isolate_name": "performance_test_suite",
+        "isolate_name": "performance_test_suite_eve",
         "merge": {
           "script": "//tools/perf/process_perf_results.py"
         },
-        "name": "performance_test_suite",
+        "name": "performance_test_suite_eve",
         "override_compile_targets": [
-          "performance_test_suite"
+          "performance_test_suite_eve"
         ],
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 813faec5..e6608795 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1104,7 +1104,6 @@
   },
   "linux-builder-perf-rel": {
     "additional_compile_targets": [
-      "chromedriver",
       "chromium_builder_perf"
     ]
   },
diff --git a/testing/buildbot/chromium.updater.json b/testing/buildbot/chromium.updater.json
index 450418ac..c0b1684598 100644
--- a/testing/buildbot/chromium.updater.json
+++ b/testing/buildbot/chromium.updater.json
@@ -132,49 +132,6 @@
       }
     ]
   },
-  "mac10.13-updater-tester-dbg": {
-    "gtest_tests": [
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Mac-10.13",
-              "pool": "chromium.updater.mac"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "updater_integration_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "updater_integration_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Mac-10.13",
-              "pool": "chromium.updater.mac"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://chrome/updater:updater_integration_tests/"
-      }
-    ]
-  },
   "mac10.13-updater-tester-rel": {
     "gtest_tests": [
       {
@@ -261,6 +218,49 @@
       }
     ]
   },
+  "mac10.15-updater-tester-dbg": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Mac-10.15",
+              "pool": "chromium.updater.mac"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "updater_tests",
+        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "updater_integration_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "updater_integration_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Mac-10.15",
+              "pool": "chromium.updater.mac"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/updater:updater_integration_tests/"
+      }
+    ]
+  },
   "mac10.15-updater-tester-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index a13f76d..62f19f5 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1276,6 +1276,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -3644,6 +3647,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -7397,6 +7403,9 @@
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
         "name": "chromedriver_py_tests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index 1dfd8e3f..5f5434d 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -24,6 +24,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -58,6 +65,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -92,6 +106,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -126,6 +147,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -169,6 +197,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -212,6 +247,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -247,6 +289,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -281,6 +330,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -316,6 +372,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
@@ -352,6 +415,13 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "MMB29Q",
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 35ca8dd..f2d2750d 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -698,6 +698,24 @@
           'script': '//testing/trigger_scripts/chromeos_device_trigger.py',
         }
 
+  def add_logdog_butler_cipd_package(self, tester_config, result):
+    if not tester_config.get('skip_cipd_packages', False):
+      cipd_packages = result['swarming'].get('cipd_packages', [])
+      already_added = len([
+          package for package in cipd_packages
+          if package.get('cipd_package', "").find('logdog/butler') > 0
+      ]) > 0
+      if not already_added:
+        cipd_packages.append({
+            'cipd_package':
+            'infra/tools/luci/logdog/butler/${platform}',
+            'location':
+            'bin',
+            'revision':
+            'git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c',
+        })
+        result['swarming']['cipd_packages'] = cipd_packages
+
   def add_android_presentation_args(self, tester_config, test_name, result):
     args = result.get('args', [])
     bucket = tester_config.get('results_bucket', 'chromium-result-details')
@@ -714,16 +732,6 @@
         'script': '//build/android/pylib/results/presentation/'
           'test_results_presentation.py',
       }
-    if not tester_config.get('skip_cipd_packages', False):
-      cipd_packages = result['swarming'].get('cipd_packages', [])
-      cipd_packages.append(
-        {
-          'cipd_package': 'infra/tools/luci/logdog/butler/${platform}',
-          'location': 'bin',
-          'revision': 'git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c',
-        }
-      )
-      result['swarming']['cipd_packages'] = cipd_packages
     if not tester_config.get('skip_output_links', False):
       result['swarming']['output_links'] = [
         {
@@ -754,10 +762,13 @@
     self.initialize_args_for_test(
         result, tester_config, additional_arg_keys=['gtest_args'])
     if self.is_android(tester_config) and tester_config.get(
-        'use_swarming',
-        True) and not test_config.get('use_isolated_scripts_api', False):
-      self.add_android_presentation_args(tester_config, test_name, result)
-      result['args'] = result.get('args', []) + ['--recover-devices']
+        'use_swarming', True):
+      if not test_config.get('use_isolated_scripts_api', False):
+        # TODO(https://crbug.com/1137998) make Android presentation work with
+        # isolated scripts in test_results_presentation.py merge script
+        self.add_android_presentation_args(tester_config, test_name, result)
+        result['args'] = result.get('args', []) + ['--recover-devices']
+      self.add_logdog_butler_cipd_package(tester_config, result)
 
     result = self.update_and_cleanup_test(
         result, test_name, tester_name, tester_config, waterfall)
@@ -788,8 +799,13 @@
     result['name'] = result.get('name', test_name)
     self.initialize_swarming_dictionary_for_test(result, tester_config)
     self.initialize_args_for_test(result, tester_config)
-    if tester_config.get('use_android_presentation', False):
-      self.add_android_presentation_args(tester_config, test_name, result)
+    if self.is_android(tester_config) and tester_config.get(
+        'use_swarming', True):
+      if tester_config.get('use_android_presentation', False):
+        # TODO(https://crbug.com/1137998) make Android presentation work with
+        # isolated scripts in test_results_presentation.py merge script
+        self.add_android_presentation_args(tester_config, test_name, result)
+      self.add_logdog_butler_cipd_package(tester_config, result)
     result = self.update_and_cleanup_test(
         result, test_name, tester_name, tester_config, waterfall)
     self.add_common_test_properties(result, tester_config)
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py
index 6999848..83c8982d 100755
--- a/testing/buildbot/generate_buildbot_json_unittest.py
+++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -248,6 +248,7 @@
     'name': 'chromium.test',
     'machines': {
       'Fake Tester': {
+        'os_type': 'android',
         'test_suites': {
           'isolated_scripts': 'composition_tests',
         },
@@ -1417,7 +1418,8 @@
             "--test-name",
             "foo_test"
           ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+          "script": \
+"//build/android/pylib/results/presentation/test_results_presentation.py"
         },
         "name": "foo_test",
         "swarming": {
@@ -1426,7 +1428,8 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": \
+"git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
             }
           ],
           "output_links": [
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 586546d..186cea8 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1439,6 +1439,10 @@
     "label": "//chrome/test:performance_test_suite",
     "type": "generated_script",
   },
+  "performance_test_suite_eve": {
+    "label": "//chrome/test:performance_test_suite_eve",
+    "type": "generated_script",
+  },
   "performance_webview_test_suite": {
     "args": [
       "remove",
@@ -1930,9 +1934,6 @@
   },
   "webview_cts_tests": {
     "args": [
-      "--logdog-bin-cmd",
-      "../../bin/logdog_butler",
-      "../../android_webview/tools/run_cts.py",
       "--skip-expected-failures",
       "--use-webview-provider",
       "apks/SystemWebView.apk",
@@ -1942,7 +1943,7 @@
       "-v",
     ],
     "label": "//android_webview/test:webview_cts_tests",
-    "script": "//build/android/test_wrapper/logdog_wrapper.py",
+    "script": "//android_webview/tools/run_cts.py",
     "type": "script",
   },
   "webview_instrumentation_test_apk": {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index a660215..a7bd0eb 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -458,9 +458,12 @@
 
     'chromedriver_py_tests_isolated_scripts': {
       'chromedriver_py_tests': {
-        "args": [
-          "--test-type=integration",
+        'args': [
+          '--test-type=integration',
         ],
+        'resultdb': {
+          'enable': True,
+        },
       },
       'chromedriver_replay_unittests': {},
     },
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index 92059ee3..20caee5 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -172,6 +172,13 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
           "dimension_sets": [
             {
               "device_os": "LMY48M",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index f2d649c3..c3eff50 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -320,13 +320,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Library Skew Tests For 89.0.4389.46',
+    'identifier': 'Implementation Library Skew Tests For 89.0.4389.48',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.46',
+          'revision': 'version:89.0.4389.48',
         }
       ],
     },
@@ -344,13 +344,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Library Skew Tests For 88.0.4324.175',
+    'identifier': 'Implementation Library Skew Tests For 88.0.4324.176',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.175',
+          'revision': 'version:88.0.4324.176',
         }
       ],
     },
@@ -392,13 +392,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Library Skew Tests For 89.0.4389.46',
+    'identifier': 'Implementation Library Skew Tests For 89.0.4389.48',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.46',
+          'revision': 'version:89.0.4389.48',
         }
       ],
     },
@@ -416,13 +416,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Library Skew Tests For 88.0.4324.175',
+    'identifier': 'Implementation Library Skew Tests For 88.0.4324.176',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.175',
+          'revision': 'version:88.0.4324.176',
         }
       ],
     },
@@ -464,13 +464,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=89',
     ],
-    'identifier': 'Client Library Skew Tests For 89.0.4389.46',
+    'identifier': 'Client Library Skew Tests For 89.0.4389.48',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.46',
+          'revision': 'version:89.0.4389.48',
         }
       ],
     },
@@ -488,13 +488,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=88',
     ],
-    'identifier': 'Client Library Skew Tests For 88.0.4324.175',
+    'identifier': 'Client Library Skew Tests For 88.0.4324.176',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.175',
+          'revision': 'version:88.0.4324.176',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 149ccd6..1b72d97 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -13,31 +13,11 @@
     'name': 'chrome',
     'mixins': ['chrome-tester-service-account'],
     'machines': {
-      'chromeos-arm-generic-beta': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-      },
       'chromeos-arm-generic-cfi-thin-lto-chrome': {
         'additional_compile_targets': [
           'chromiumos_preflight',
         ],
       },
-      'chromeos-arm-generic-ltc': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-      },
-      'chromeos-arm-generic-lts': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-      },
-      'chromeos-arm-generic-stable': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-      },
       'chromeos-betty-pi-arc-cfi-thin-lto-chrome': {
         'additional_compile_targets': [
           'chromiumos_preflight',
@@ -5635,20 +5615,6 @@
           ]
         },
       },
-      'mac10.13-updater-tester-dbg': {
-        'test_suites': {
-          'gtest_tests': 'updater_gtests',
-          'isolated_scripts': 'updater_isolated_scripts',
-        },
-        'swarming': {
-          'dimension_sets': [
-            {
-              'pool': 'chromium.updater.mac',
-              'os': 'Mac-10.13',
-            }
-          ]
-        },
-      },
       'mac10.13-updater-tester-rel': {
         'test_suites': {
           'gtest_tests': 'updater_gtests',
@@ -5677,6 +5643,20 @@
           ]
         },
       },
+      'mac10.15-updater-tester-dbg': {
+        'test_suites': {
+          'gtest_tests': 'updater_gtests',
+          'isolated_scripts': 'updater_isolated_scripts',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'pool': 'chromium.updater.mac',
+              'os': 'Mac-10.15',
+            }
+          ]
+        },
+      },
       'mac10.15-updater-tester-rel': {
         'test_suites': {
           'gtest_tests': 'updater_gtests',
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index ada2604..4c23f81 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -363,7 +363,7 @@
     Returns:
       list of strings, the executable and its arguments.
     """
-    return ([sys.executable, self._options.executable] +
+    return ([sys.executable] + self._options.executable.split(' ') +
             [self.benchmark] +
             self._generate_filter_args() +
             self._generate_also_run_disabled_tests_args() +
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7f22e352..bd1bdfd 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3264,6 +3264,22 @@
             ]
         }
     ],
+    "FormControlsRefreshAndroid": [
+        {
+            "platforms": [
+                "android",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "FormControlsRefresh"
+                    ]
+                }
+            ]
+        }
+    ],
     "FreezeBackgroundTabOnNetworkIdle": [
         {
             "platforms": [
@@ -5751,28 +5767,6 @@
             ]
         }
     ],
-    "ReparseServerPredictionsFollowingFormChange": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ReparseServerPredictionsFollowingFormChange"
-                    ]
-                }
-            ]
-        }
-    ],
     "ReportCertificateErrors": [
         {
             "platforms": [
@@ -5932,7 +5926,7 @@
             ]
         }
     ],
-    "SafeBrowsingEnhancedProtectionAndroid": [
+    "SafeBrowsingPasswordProtectionReferringAppEnabledAndroid": [
         {
             "platforms": [
                 "android"
@@ -5941,29 +5935,7 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "PrivacyReorderedAndroid",
-                        "SafeBrowsingEnhancedProtection",
-                        "SafeBrowsingSecuritySectionUIAndroid"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SafeBrowsingEnhancedProtectionMessageInInterstitials": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SafeBrowsingEnhancedProtectionMessageInInterstitials"
+                        "SafeBrowsingPasswordProtectionReferringAppEnabledAndroid"
                     ]
                 }
             ]
@@ -6006,21 +5978,6 @@
             ]
         }
     ],
-    "SafeBrowsingRealTimeUrlLookupEnabledWithTokenAndroid": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SafeBrowsingRealTimeUrlLookupEnabledWithToken"
-                    ]
-                }
-            ]
-        }
-    ],
     "SafetyAndPasswordCheckAndroid": [
         {
             "platforms": [
@@ -6142,28 +6099,6 @@
             ]
         }
     ],
-    "SecondaryServerFieldPredictions": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SecondaryServerFieldPredictions"
-                    ]
-                }
-            ]
-        }
-    ],
     "ServiceManagerForBackgroundPrefetch": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index e03d2e0..fed5bb89 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -56,6 +56,7 @@
 /checkstyle/*.jar
 /chromeos_login_manager
 /chromeos_text_input
+/chromevox/third_party/sre/src
 /chromite
 /cld_2/src
 /cld_3/src
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 050e3386..6f0b62b 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -53,6 +53,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_session_description_callback.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_stats_callback.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_stats_callback.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_callback.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_callback.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_state_callback.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_state_callback.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_storage_error_callback.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index c93c96c..b11cb53 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -601,6 +601,7 @@
           "//third_party/blink/renderer/modules/sanitizer_api/sanitizer.idl",
           "//third_party/blink/renderer/modules/sanitizer_api/sanitizer_config.idl",
           "//third_party/blink/renderer/modules/scheduler/scheduler.idl",
+          "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl",
           "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl",
           "//third_party/blink/renderer/modules/scheduler/task_controller.idl",
           "//third_party/blink/renderer/modules/scheduler/task_signal.idl",
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
index 12c18f3..936b3fbf 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
@@ -155,12 +155,14 @@
     const float kSlack = 1e-6;
     const float before_length = FloatValueForLength(before, 100);
     const float after_length = FloatValueForLength(after, 100);
-    // Test relative difference for large values to avoid floating point
-    // inaccuracies tripping the check.
-    const float delta = std::abs(before_length) < kSlack
-                            ? after_length - before_length
-                            : (after_length - before_length) / before_length;
-    DCHECK_LT(std::abs(delta), kSlack);
+    if (std::isfinite(before_length) && std::isfinite(after_length)) {
+      // Test relative difference for large values to avoid floating point
+      // inaccuracies tripping the check.
+      const float delta = std::abs(before_length) < kSlack
+                              ? after_length - before_length
+                              : (after_length - before_length) / before_length;
+      DCHECK_LT(std::abs(delta), kSlack);
+    }
 #endif
     return;
   }
diff --git a/third_party/blink/renderer/core/css/counter_style.cc b/third_party/blink/renderer/core/css/counter_style.cc
index 71fc781..a42437c 100644
--- a/third_party/blink/renderer/core/css/counter_style.cc
+++ b/third_party/blink/renderer/core/css/counter_style.cc
@@ -400,7 +400,7 @@
 }
 
 CounterStyle::CounterStyle(const StyleRuleCounterStyle& rule)
-    : style_rule_(rule) {
+    : style_rule_(rule), style_rule_version_(rule.GetVersion()) {
   if (const CSSValue* system = rule.GetSystem()) {
     system_ = ToCounterStyleSystemEnum(system);
 
@@ -685,7 +685,8 @@
     return;
   visited_counter_styles.insert(this);
 
-  if (has_inexistent_references_) {
+  if (has_inexistent_references_ ||
+      style_rule_version_ != style_rule_->GetVersion()) {
     SetIsDirty();
     return;
   }
diff --git a/third_party/blink/renderer/core/css/counter_style.h b/third_party/blink/renderer/core/css/counter_style.h
index 83317946..3058e1ab 100644
--- a/third_party/blink/renderer/core/css/counter_style.h
+++ b/third_party/blink/renderer/core/css/counter_style.h
@@ -114,6 +114,9 @@
   // The corresponding style rule in CSS.
   Member<const StyleRuleCounterStyle> style_rule_;
 
+  // Tracks mutations of |style_rule_|.
+  int style_rule_version_;
+
   // The actual system of the counter style with 'extends' resolved. The value
   // is kUnresolvedExtends temporarily before the resolution.
   CounterStyleSystem system_ = CounterStyleSystem::kSymbolic;
diff --git a/third_party/blink/renderer/core/css/css_counter_style_rule.cc b/third_party/blink/renderer/core/css/css_counter_style_rule.cc
index 2d09aec..366180df 100644
--- a/third_party/blink/renderer/core/css/css_counter_style_rule.cc
+++ b/third_party/blink/renderer/core/css/css_counter_style_rule.cc
@@ -3,7 +3,13 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/css_counter_style_rule.h"
+#include "third_party/blink/renderer/core/css/css_style_sheet.h"
+#include "third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
+#include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_rule_counter_style.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -165,48 +171,124 @@
   return String();
 }
 
+void CSSCounterStyleRule::SetterInternal(
+    const ExecutionContext* execution_context,
+    AtRuleDescriptorID descriptor_id,
+    const String& text) {
+  CSSStyleSheet* style_sheet = parentStyleSheet();
+  auto& context = *MakeGarbageCollected<CSSParserContext>(
+      ParserContext(execution_context->GetSecureContextMode()), style_sheet);
+  CSSTokenizer tokenizer(text);
+  auto tokens = tokenizer.TokenizeToEOF();
+  CSSParserTokenRange token_range(tokens);
+  CSSValue* new_value = AtRuleDescriptorParser::ParseAtCounterStyleDescriptor(
+      descriptor_id, token_range, context);
+  if (!new_value)
+    return;
+
+  switch (descriptor_id) {
+    case AtRuleDescriptorID::System:
+      if (!counter_style_rule_->SetSystem(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Negative:
+      if (!counter_style_rule_->SetNegative(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Prefix:
+      if (!counter_style_rule_->SetPrefix(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Suffix:
+      if (!counter_style_rule_->SetSuffix(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Range:
+      if (!counter_style_rule_->SetRange(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Pad:
+      if (!counter_style_rule_->SetPad(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Fallback:
+      if (!counter_style_rule_->SetFallback(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::Symbols:
+      if (!counter_style_rule_->SetSymbols(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::AdditiveSymbols:
+      if (!counter_style_rule_->SetAdditiveSymbols(new_value))
+        return;
+      break;
+    case AtRuleDescriptorID::SpeakAs:
+      if (!counter_style_rule_->SetSpeakAs(new_value))
+        return;
+      break;
+    default:
+      NOTREACHED();
+      return;
+  }
+
+  if (Document* document = style_sheet->OwnerDocument())
+    document->GetStyleEngine().MarkCounterStylesNeedUpdate();
+}
+
 void CSSCounterStyleRule::setName(const String&) {
   // TODO(crbug.com/687225): Implement
 }
 
-void CSSCounterStyleRule::setSystem(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setSystem(const ExecutionContext* execution_context,
+                                    const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::System, text);
 }
 
-void CSSCounterStyleRule::setSymbols(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setSymbols(const ExecutionContext* execution_context,
+                                     const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Symbols, text);
 }
 
-void CSSCounterStyleRule::setAdditiveSymbols(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setAdditiveSymbols(
+    const ExecutionContext* execution_context,
+    const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::AdditiveSymbols, text);
 }
 
-void CSSCounterStyleRule::setNegative(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setNegative(const ExecutionContext* execution_context,
+                                      const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Negative, text);
 }
 
-void CSSCounterStyleRule::setPrefix(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setPrefix(const ExecutionContext* execution_context,
+                                    const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Prefix, text);
 }
 
-void CSSCounterStyleRule::setSuffix(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setSuffix(const ExecutionContext* execution_context,
+                                    const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Suffix, text);
 }
 
-void CSSCounterStyleRule::setRange(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setRange(const ExecutionContext* execution_context,
+                                   const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Range, text);
 }
 
-void CSSCounterStyleRule::setPad(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setPad(const ExecutionContext* execution_context,
+                                 const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Pad, text);
 }
 
-void CSSCounterStyleRule::setSpeakAs(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setSpeakAs(const ExecutionContext* execution_context,
+                                     const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::SpeakAs, text);
 }
 
-void CSSCounterStyleRule::setFallback(const String&) {
-  // TODO(crbug.com/687225): Implement
+void CSSCounterStyleRule::setFallback(const ExecutionContext* execution_context,
+                                      const String& text) {
+  SetterInternal(execution_context, AtRuleDescriptorID::Fallback, text);
 }
 
 void CSSCounterStyleRule::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/css/css_counter_style_rule.h b/third_party/blink/renderer/core/css/css_counter_style_rule.h
index 7581cb9c..524ef61 100644
--- a/third_party/blink/renderer/core/css/css_counter_style_rule.h
+++ b/third_party/blink/renderer/core/css/css_counter_style_rule.h
@@ -6,10 +6,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_COUNTER_STYLE_RULE_H_
 
 #include "third_party/blink/renderer/core/css/css_rule.h"
+#include "third_party/blink/renderer/core/css/parser/at_rule_descriptors.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
+class ExecutionContext;
 class StyleRuleCounterStyle;
 
 class CSSCounterStyleRule final : public CSSRule {
@@ -35,22 +37,26 @@
   String fallback() const;
 
   void setName(const String&);
-  void setSystem(const String&);
-  void setSymbols(const String&);
-  void setAdditiveSymbols(const String&);
-  void setNegative(const String&);
-  void setPrefix(const String&);
-  void setSuffix(const String&);
-  void setRange(const String&);
-  void setPad(const String&);
-  void setSpeakAs(const String&);
-  void setFallback(const String&);
+  void setSystem(const ExecutionContext*, const String&);
+  void setSymbols(const ExecutionContext*, const String&);
+  void setAdditiveSymbols(const ExecutionContext*, const String&);
+  void setNegative(const ExecutionContext*, const String&);
+  void setPrefix(const ExecutionContext*, const String&);
+  void setSuffix(const ExecutionContext*, const String&);
+  void setRange(const ExecutionContext*, const String&);
+  void setPad(const ExecutionContext*, const String&);
+  void setSpeakAs(const ExecutionContext*, const String&);
+  void setFallback(const ExecutionContext*, const String&);
 
   void Trace(Visitor*) const override;
 
  private:
   CSSRule::Type GetType() const override { return kCounterStyleRule; }
 
+  void SetterInternal(const ExecutionContext*,
+                      AtRuleDescriptorID,
+                      const String&);
+
   Member<StyleRuleCounterStyle> counter_style_rule_;
 };
 
diff --git a/third_party/blink/renderer/core/css/css_counter_style_rule.idl b/third_party/blink/renderer/core/css/css_counter_style_rule.idl
index 4b1f8ef..c8147ae6 100644
--- a/third_party/blink/renderer/core/css/css_counter_style_rule.idl
+++ b/third_party/blink/renderer/core/css/css_counter_style_rule.idl
@@ -6,14 +6,14 @@
 [Exposed=Window, RuntimeEnabled=CSSAtRuleCounterStyle]
 interface CSSCounterStyleRule : CSSRule {
   attribute CSSOMString name;
-  attribute CSSOMString system;
-  attribute CSSOMString symbols;
-  attribute CSSOMString additiveSymbols;
-  attribute CSSOMString negative;
-  attribute CSSOMString prefix;
-  attribute CSSOMString suffix;
-  attribute CSSOMString range;
-  attribute CSSOMString pad;
-  attribute CSSOMString speakAs;
-  attribute CSSOMString fallback;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString system;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString symbols;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString additiveSymbols;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString negative;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString prefix;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString suffix;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString range;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString pad;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString speakAs;
+  [SetterCallWith=ExecutionContext] attribute CSSOMString fallback;
 };
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index caf654b4..f7821e93 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -1585,10 +1585,10 @@
 
 const CSSValue* ClipRule::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.ClipRule());
+  return CSSIdentifierValue::Create(style.ClipRule());
 }
 
 const CSSValue* Color::ParseSingleValue(CSSParserTokenRange& range,
@@ -1650,26 +1650,26 @@
 
 const CSSValue* ColorInterpolation::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.ColorInterpolation());
+  return CSSIdentifierValue::Create(style.ColorInterpolation());
 }
 
 const CSSValue* ColorInterpolationFilters::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.ColorInterpolationFilters());
+  return CSSIdentifierValue::Create(style.ColorInterpolationFilters());
 }
 
 const CSSValue* ColorRendering::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.ColorRendering());
+  return CSSIdentifierValue::Create(style.ColorRendering());
 }
 
 const CSSValue* ColorScheme::ParseSingleValue(
@@ -2655,10 +2655,10 @@
 
 const CSSValue* DominantBaseline::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.DominantBaseline());
+  return CSSIdentifierValue::Create(style.DominantBaseline());
 }
 
 const CSSValue* EmptyCells::CSSValueFromComputedStyleInternal(
@@ -2712,10 +2712,10 @@
 
 const CSSValue* FillRule::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.FillRule());
+  return CSSIdentifierValue::Create(style.FillRule());
 }
 
 const CSSValue* Filter::ParseSingleValue(CSSParserTokenRange& range,
@@ -5474,11 +5474,11 @@
 }
 
 const CSSValue* PaintOrder::CSSValueFromComputedStyleInternal(
-    const ComputedStyle&,
-    const SVGComputedStyle& svg_style,
+    const ComputedStyle& style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  const EPaintOrder paint_order = svg_style.PaintOrder();
+  const EPaintOrder paint_order = style.PaintOrder();
   if (paint_order == kPaintOrderNormal)
     return CSSIdentifierValue::Create(CSSValueID::kNormal);
 
@@ -6345,10 +6345,10 @@
 
 const CSSValue* ShapeRendering::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.ShapeRendering());
+  return CSSIdentifierValue::Create(style.ShapeRendering());
 }
 
 static CSSValue* ConsumePageSize(CSSParserTokenRange& range) {
@@ -6784,10 +6784,10 @@
 
 const CSSValue* TextAnchor::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
-    const SVGComputedStyle& svg_style,
+    const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
-  return CSSIdentifierValue::Create(svg_style.TextAnchor());
+  return CSSIdentifierValue::Create(style.TextAnchor());
 }
 
 const CSSValue* TextCombineUpright::CSSValueFromComputedStyleInternal(
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index dd570c4e..84f1f72 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -546,13 +546,6 @@
     // Moving the initialization to other places causes test failures, which
     // needs investigation and fixing.
     CounterStyleMap::GetUACounterStyleMap();
-
-    if (counter_styles_need_update_) {
-      CounterStyleMap::MarkAllDirtyCounterStyles(GetDocument(),
-                                                 active_tree_scopes_);
-      CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_);
-      counter_styles_need_update_ = false;
-    }
   }
 
   probe::ActiveStyleSheetsUpdated(document_);
@@ -563,6 +556,16 @@
   user_style_dirty_ = false;
 }
 
+void StyleEngine::UpdateCounterStyles() {
+  if (!counter_styles_need_update_)
+    return;
+  DCHECK(RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled());
+  CounterStyleMap::MarkAllDirtyCounterStyles(GetDocument(),
+                                             active_tree_scopes_);
+  CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_);
+  counter_styles_need_update_ = false;
+}
+
 void StyleEngine::UpdateViewport() {
   if (viewport_resolver_)
     viewport_resolver_->UpdateViewport(GetDocumentStyleSheetCollection());
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 16f6c6c2..595ba53 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -378,6 +378,7 @@
   void InvalidateStyleAndLayoutForFontUpdates();
 
   void MarkCounterStylesNeedUpdate();
+  void UpdateCounterStyles();
 
   StyleRuleKeyframes* KeyframeStylesForAnimation(
       const AtomicString& animation_name);
diff --git a/third_party/blink/renderer/core/css/style_rule_counter_style.h b/third_party/blink/renderer/core/css/style_rule_counter_style.h
index c3c76e8..e45bfa3bd 100644
--- a/third_party/blink/renderer/core/css/style_rule_counter_style.h
+++ b/third_party/blink/renderer/core/css/style_rule_counter_style.h
@@ -15,6 +15,8 @@
   StyleRuleCounterStyle(const StyleRuleCounterStyle&);
   ~StyleRuleCounterStyle();
 
+  int GetVersion() const { return version_; }
+
   AtomicString GetName() const { return name_; }
   const CSSValue* GetSystem() const { return system_; }
   const CSSValue* GetNegative() const { return negative_; }
@@ -27,19 +29,58 @@
   const CSSValue* GetAdditiveSymbols() const { return additive_symbols_; }
   const CSSValue* GetSpeakAs() const { return speak_as_; }
 
-  void SetName(const AtomicString& name) { name_ = name; }
-  void SetSystem(const CSSValue* system) { system_ = system; }
-  void SetNegative(const CSSValue* negative) { negative_ = negative; }
-  void SetPrefix(const CSSValue* prefix) { prefix_ = prefix; }
-  void SetSuffix(const CSSValue* suffix) { suffix_ = suffix; }
-  void SetRange(const CSSValue* range) { range_ = range; }
-  void SetPad(const CSSValue* pad) { pad_ = pad; }
-  void SetFallback(const CSSValue* fallback) { fallback_ = fallback; }
-  void SetSymbols(const CSSValue* symbols) { symbols_ = symbols; }
-  void SetAdditiveSymbols(const CSSValue* additive_symbols) {
-    additive_symbols_ = additive_symbols;
+  // Returns false if the setter fails due to invalid new value.
+  bool SetName(const AtomicString& name) {
+    // TODO(crbug.com/687225): Implement.
+    return false;
   }
-  void SetSpeakAs(const CSSValue* speak_as) { speak_as_ = speak_as; }
+  bool SetSystem(const CSSValue* system) {
+    // TODO(crbug.com/687225): Implement.
+    return false;
+  }
+  bool SetNegative(const CSSValue* negative) {
+    negative_ = negative;
+    ++version_;
+    return true;
+  }
+  bool SetPrefix(const CSSValue* prefix) {
+    prefix_ = prefix;
+    ++version_;
+    return true;
+  }
+  bool SetSuffix(const CSSValue* suffix) {
+    suffix_ = suffix;
+    ++version_;
+    return true;
+  }
+  bool SetRange(const CSSValue* range) {
+    range_ = range;
+    ++version_;
+    return true;
+  }
+  bool SetPad(const CSSValue* pad) {
+    pad_ = pad;
+    ++version_;
+    return true;
+  }
+  bool SetFallback(const CSSValue* fallback) {
+    fallback_ = fallback;
+    ++version_;
+    return true;
+  }
+  bool SetSymbols(const CSSValue* symbols) {
+    // TODO(crbug.com/687225): Implement.
+    return false;
+  }
+  bool SetAdditiveSymbols(const CSSValue* additive_symbols) {
+    // TODO(crbug.com/687225): Implement.
+    return false;
+  }
+  bool SetSpeakAs(const CSSValue* speak_as) {
+    speak_as_ = speak_as;
+    ++version_;
+    return true;
+  }
 
   bool HasFailedOrCanceledSubresources() const {
     // TODO(crbug.com/687225): Implement.
@@ -64,6 +105,9 @@
   Member<const CSSValue> symbols_;
   Member<const CSSValue> additive_symbols_;
   Member<const CSSValue> speak_as_;
+
+  // Tracks mutations due to setter functions.
+  int version_ = 0;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 682af22..b576a68f 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2358,6 +2358,7 @@
   UpdateUseShadowTreesIfNeeded();
 
   GetStyleEngine().UpdateActiveStyle();
+  GetStyleEngine().UpdateCounterStyles();
   InvalidateStyleAndLayoutForFontUpdates();
   UpdateStyleInvalidationIfNeeded();
   UpdateStyle();
@@ -3309,49 +3310,56 @@
   if (ignore_opens_and_writes_for_abort_)
     return;
 
-  // If this document is fully active, change |document|'s URL to the URL of the
-  // responsible document specified by the entry settings object.
-  if (dom_window_ && entered_window && dom_window_ != entered_window) {
-    auto* csp = MakeGarbageCollected<ContentSecurityPolicy>();
-    csp->CopyStateFrom(entered_window->GetContentSecurityPolicy());
-    // We inherit the sandbox flags of the entered document, so mask on
-    // the ones contained in the CSP. The operator| is a bitwise operation on
-    // the sandbox flags bits. It makes the sandbox policy stricter (or as
-    // strict) as both policy.
-    //
-    // TODO(arthursonzogni): Why merging sandbox flags?
-    // This doesn't look great at many levels:
-    // - The browser process won't be notified of the update.
-    // - The origin won't be made opaque, despite the new flags.
-    // - The sandbox flags of the document can't be considered to be an
-    //   immutable property anymore.
-    //
-    // Ideally:
-    // - javascript-url document.
-    // - XSLT document.
-    // - document.open.
-    // should not mutate the security properties of the current document. From
-    // the browser process point of view, all of those operations are not
-    // considered to produce new documents. No IPCs are sent, it is as if it was
-    // a no-op.
-    dom_window_->GetSecurityContext().SetSandboxFlags(
-        dom_window_->GetSecurityContext().GetSandboxFlags() |
-        entered_window->GetSandboxFlags());
-    dom_window_->GetSecurityContext().SetContentSecurityPolicy(csp);
-    dom_window_->GetContentSecurityPolicy()->BindToDelegate(
-        dom_window_->GetContentSecurityPolicyDelegate());
+  // If this document is fully active, then run the URL and history update steps
+  // for this document with the entered window's url.
+  if (dom_window_ && entered_window) {
+    KURL new_url = entered_window->Url();
     // Clear the hash fragment from the inherited URL to prevent a
     // scroll-into-view for any document.open()'d frame.
-    KURL new_url = entered_window->Url();
-    new_url.SetFragmentIdentifier(String());
+    if (dom_window_ != entered_window)
+      new_url.SetFragmentIdentifier(String());
     SetURL(new_url);
-    if (Loader())
-      Loader()->UpdateUrlForDocumentOpen(new_url);
 
-    dom_window_->GetSecurityContext().SetSecurityOrigin(
-        entered_window->GetMutableSecurityOrigin());
-    dom_window_->SetReferrerPolicy(entered_window->GetReferrerPolicy());
-    cookie_url_ = entered_window->document()->CookieURL();
+    auto* state_object = Loader()->GetHistoryItem()
+                             ? Loader()->GetHistoryItem()->StateObject()
+                             : nullptr;
+    Loader()->RunURLAndHistoryUpdateSteps(new_url, state_object);
+
+    if (dom_window_ != entered_window) {
+      auto* csp = MakeGarbageCollected<ContentSecurityPolicy>();
+      csp->CopyStateFrom(entered_window->GetContentSecurityPolicy());
+      // We inherit the sandbox flags of the entered document, so mask on
+      // the ones contained in the CSP. The operator| is a bitwise operation on
+      // the sandbox flags bits. It makes the sandbox policy stricter (or as
+      // strict) as both policy.
+      //
+      // TODO(arthursonzogni): Why merging sandbox flags?
+      // This doesn't look great at many levels:
+      // - The browser process won't be notified of the update.
+      // - The origin won't be made opaque, despite the new flags.
+      // - The sandbox flags of the document can't be considered to be an
+      //   immutable property anymore.
+      //
+      // Ideally:
+      // - javascript-url document.
+      // - XSLT document.
+      // - document.open.
+      // should not mutate the security properties of the current document. From
+      // the browser process point of view, all of those operations are not
+      // considered to produce new documents. No IPCs are sent, it is as if it
+      // was a no-op.
+      dom_window_->GetSecurityContext().SetSandboxFlags(
+          dom_window_->GetSecurityContext().GetSandboxFlags() |
+          entered_window->GetSandboxFlags());
+      dom_window_->GetSecurityContext().SetContentSecurityPolicy(csp);
+      dom_window_->GetContentSecurityPolicy()->BindToDelegate(
+          dom_window_->GetContentSecurityPolicyDelegate());
+
+      dom_window_->GetSecurityContext().SetSecurityOrigin(
+          entered_window->GetMutableSecurityOrigin());
+      dom_window_->SetReferrerPolicy(entered_window->GetReferrerPolicy());
+      cookie_url_ = entered_window->document()->CookieURL();
+    }
   }
 
   open();
@@ -7305,28 +7313,21 @@
   return nullptr;
 }
 
-void Document::PushNewPopupElement(HTMLPopupElement* popup) {
-  DCHECK(!popup_element_stack_.Contains(popup));
-  popup_element_stack_.push_back(popup);
-  AddToTopLayer(popup);
+bool Document::PopupShowing() const {
+  return !popup_element_stack_.IsEmpty();
 }
-
-void Document::PopPopupElement(HTMLPopupElement* popup) {
-  DCHECK(popup_element_stack_.back() == popup);
-  popup_element_stack_.pop_back();
-  RemoveFromTopLayer(popup);
+void Document::HideTopmostPopupElement() const {
+  if (popup_element_stack_.IsEmpty())
+    return;
+  popup_element_stack_.back()->hide();
 }
-
-HTMLPopupElement* Document::TopmostPopupElement() {
-  return popup_element_stack_.IsEmpty() ? nullptr : popup_element_stack_.back();
-}
-
 void Document::HideAllPopupsUntil(HTMLPopupElement* endpoint) {
-  HTMLPopupElement* element;
-  while ((element = TopmostPopupElement()) != endpoint) {
-    DCHECK(element) << "popup element not found in element stack";
-    element->hide();
+  while (!popup_element_stack_.IsEmpty() &&
+         popup_element_stack_.back() != endpoint) {
+    popup_element_stack_.back()->hide();
   }
+  DCHECK(!endpoint || popup_element_stack_.back() == endpoint)
+      << "popup element not found in element stack";
 }
 
 void Document::exitPointerLock() {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 2509ca5f..7d28874 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1376,22 +1376,21 @@
   const HeapVector<Member<Element>>& TopLayerElements() const {
     return top_layer_elements_;
   }
-  const HeapVector<Member<HTMLPopupElement>>& PopupElementStack() const {
+
+  HTMLDialogElement* ActiveModalDialog() const;
+
+  HeapVector<Member<HTMLPopupElement>>& PopupElementStack() {
     return popup_element_stack_;
   }
+  bool PopupShowing() const;
+  void HideTopmostPopupElement() const;
   // This hides all visible popups up to, but not including,
   // |endpoint|. If |endpoint| is nullptr, all popups are hidden.
   void HideAllPopupsUntil(HTMLPopupElement* endpoint);
 
-  HTMLDialogElement* ActiveModalDialog() const;
-
-  void PushNewPopupElement(HTMLPopupElement*);
-  void PopPopupElement(HTMLPopupElement*);
-  HTMLPopupElement* TopmostPopupElement();
-
   // A non-null template_document_host_ implies that |this| was created by
   // EnsureTemplateDocument().
-  bool IsTemplateDocument() const { return !!template_document_host_; }
+  bool IsTemplateDocument() const { return template_document_host_; }
   Document& EnsureTemplateDocument();
   Document* TemplateDocumentHost() { return template_document_host_; }
 
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index 9ad270e3..a43dcce 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -217,7 +217,30 @@
 }
 
 const KURL& DocumentInit::GetCookieUrl() const {
-  return owner_document_ ? owner_document_->CookieURL() : url_;
+  const KURL& cookie_url =
+      owner_document_ ? owner_document_->CookieURL() : url_;
+
+  // An "about:blank" should inherit the `cookie_url` from the initiator of the
+  // navigation, but sometimes "about:blank" may commit without an
+  // `owner_document` (e.g. if the original initiator has been navigated away).
+  // In such scenario, it is important to use a safe `cookie_url` (e.g.
+  // kCookieAverseUrl) to avoid triggering mojo::ReportBadMessage and renderer
+  // kills via RestrictedCookieManager::ValidateAccessToCookiesAt.
+  //
+  // TODO(https://crbug.com/1176291): Correctly inherit the `cookie_url` from
+  // the initiator.
+  if (cookie_url.IsAboutBlankURL()) {
+    // Signify a cookie-averse document [1] with an null URL.  See how
+    // CookiesJar::GetCookies and other methods check `cookie_url` against
+    // KURL::IsEmpty.
+    //
+    // [1] https://html.spec.whatwg.org/#cookie-averse-document-object
+    const KURL& kCookieAverseUrl = NullURL();
+
+    return kCookieAverseUrl;
+  }
+
+  return cookie_url;
 }
 
 DocumentInit& DocumentInit::WithSrcdocDocument(bool is_srcdoc_document) {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index caf2a77..13caf93f 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -101,6 +101,7 @@
 #include "third_party/blink/renderer/core/html/custom/custom_element.h"
 #include "third_party/blink/renderer/core/html/html_dialog_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
+#include "third_party/blink/renderer/core/html/html_popup_element.h"
 #include "third_party/blink/renderer/core/html/html_slot_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -2853,6 +2854,24 @@
 }
 
 void Node::HandleLocalEvents(Event& event) {
+  if (IsDocumentNode() && GetDocument().PopupShowing() &&
+      event.eventPhase() == Event::kCapturingPhase) {
+    // There is a popup visible - check if this event should "light dismiss"
+    // one or more popups.
+    const AtomicString& event_type = event.type();
+    if (event_type == event_type_names::kClick) {
+      HTMLPopupElement* closest_popup_parent = nullptr;
+      for (Node* current_node = event.target()->ToNode(); current_node;
+           current_node = current_node->parentNode()) {
+        if (auto* popup = DynamicTo<HTMLPopupElement>(current_node)) {
+          closest_popup_parent = popup;
+          break;
+        }
+      }
+      GetDocument().HideAllPopupsUntil(closest_popup_parent);
+    }
+  }
+
   if (!HasEventTargetData())
     return;
 
diff --git a/third_party/blink/renderer/core/frame/history.cc b/third_party/blink/renderer/core/frame/history.cc
index 2db3ecfc..1c0cc93 100644
--- a/third_party/blink/renderer/core/frame/history.cc
+++ b/third_party/blink/renderer/core/frame/history.cc
@@ -341,9 +341,8 @@
     return;
   }
 
-  DomWindow()->document()->Loader()->UpdateForSameDocumentNavigation(
-      full_url, kSameDocumentNavigationHistoryApi, std::move(data),
-      restoration_type, type, DomWindow()->document());
+  DomWindow()->document()->Loader()->RunURLAndHistoryUpdateSteps(
+      full_url, std::move(data), restoration_type, type);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index bc6f08d9..ec38e8ed 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1139,9 +1139,8 @@
     return false;
 
   // The command line flag --disable-accelerated-2d-canvas toggles this option
-  if (!RuntimeEnabledFeatures::Accelerated2dCanvasEnabled()) {
+  if (!RuntimeEnabledFeatures::Accelerated2dCanvasEnabled())
     return false;
-  }
 
   // Webview crashes with accelerated small canvases (crbug.com/1004304)
   // Experimenting to see if this still causes crashes (crbug.com/1136603)
diff --git a/third_party/blink/renderer/core/html/html_popup_element.cc b/third_party/blink/renderer/core/html/html_popup_element.cc
index c05bbf7a..9a6757c 100644
--- a/third_party/blink/renderer/core/html/html_popup_element.cc
+++ b/third_party/blink/renderer/core/html/html_popup_element.cc
@@ -32,7 +32,7 @@
   if (!open_)
     return;
   GetDocument().HideAllPopupsUntil(this);
-  GetDocument().PopPopupElement(this);
+  PopPopupElement(this);
   open_ = false;
   PseudoStateChanged(CSSSelector::kPseudoPopupOpen);
   MarkStyleDirty();
@@ -65,10 +65,29 @@
   GetDocument().HideAllPopupsUntil(parent_popup);
   open_ = true;
   PseudoStateChanged(CSSSelector::kPseudoPopupOpen);
-  GetDocument().PushNewPopupElement(this);
+  PushNewPopupElement(this);
   MarkStyleDirty();
 }
 
+void HTMLPopupElement::PushNewPopupElement(HTMLPopupElement* popup) {
+  auto& stack = GetDocument().PopupElementStack();
+  DCHECK(!stack.Contains(popup));
+  stack.push_back(popup);
+  GetDocument().AddToTopLayer(popup);
+}
+
+void HTMLPopupElement::PopPopupElement(HTMLPopupElement* popup) {
+  auto& stack = GetDocument().PopupElementStack();
+  DCHECK(stack.back() == popup);
+  stack.pop_back();
+  GetDocument().RemoveFromTopLayer(popup);
+}
+
+HTMLPopupElement* HTMLPopupElement::TopmostPopupElement() {
+  auto& stack = GetDocument().PopupElementStack();
+  return stack.IsEmpty() ? nullptr : stack.back();
+}
+
 Element* HTMLPopupElement::AnchorElement() const {
   const AtomicString& anchor_id = FastGetAttribute(html_names::kAnchorAttr);
   if (anchor_id.IsNull())
diff --git a/third_party/blink/renderer/core/html/html_popup_element.h b/third_party/blink/renderer/core/html/html_popup_element.h
index a38c76b..b6e4ec7 100644
--- a/third_party/blink/renderer/core/html/html_popup_element.h
+++ b/third_party/blink/renderer/core/html/html_popup_element.h
@@ -34,6 +34,10 @@
   void ScheduleHideEvent();
   void MarkStyleDirty();
 
+  void PushNewPopupElement(HTMLPopupElement*);
+  void PopPopupElement(HTMLPopupElement*);
+  HTMLPopupElement* TopmostPopupElement();
+
   bool open_;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 34f7f61..206c9698 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -27,7 +27,7 @@
 
 scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() {
   // Measure items.
-  Vector<GridItemData> grid_items;
+  GridItems grid_items;
   Vector<GridItemData> out_of_flow_items;
   ConstructAndAppendGridItems(&grid_items, &out_of_flow_items);
 
@@ -40,23 +40,16 @@
   BuildAlgorithmTrackCollections(&grid_items, &column_track_collection,
                                  &row_track_collection, &grid_placement);
 
-  // Create a vector of grid item indices using |NGGridChildIterator| order.
-  Vector<wtf_size_t> reordered_item_indices(grid_items.size());
-  for (wtf_size_t i = 0; i < grid_items.size(); ++i)
-    reordered_item_indices[i] = i;
-
   // Cache track span properties for grid items.
-  CacheGridItemsTrackSpanProperties(column_track_collection, &grid_items,
-                                    &reordered_item_indices);
-  CacheGridItemsTrackSpanProperties(row_track_collection, &grid_items,
-                                    &reordered_item_indices);
+  CacheGridItemsTrackSpanProperties(column_track_collection, &grid_items);
+  CacheGridItemsTrackSpanProperties(row_track_collection, &grid_items);
 
   // Resolve inline size.
   ComputeUsedTrackSizes(SizingConstraint::kLayout, &column_track_collection,
-                        &grid_items, &reordered_item_indices);
+                        &grid_items);
   // Resolve block size.
   ComputeUsedTrackSizes(SizingConstraint::kLayout, &row_track_collection,
-                        &grid_items, &reordered_item_indices);
+                        &grid_items);
 
   // Determine the final (used) set geometry.
   const SetGeometry column_set_geometry = ComputeSetGeometry(
@@ -92,7 +85,7 @@
   }
 
   // Cache set indices for grid items, as all of them will be used.
-  for (GridItemData& grid_item : grid_items) {
+  for (auto& grid_item : grid_items.item_data) {
     grid_item.SetIndices(column_track_collection);
     grid_item.SetIndices(row_track_collection);
   }
@@ -141,8 +134,8 @@
   // TODO(janewman): Handle the cases typically done via:
   // CalculateMinMaxSizesIgnoringChildren.
 
-  // Measure Items
-  Vector<GridItemData> grid_items;
+  // Measure items.
+  GridItems grid_items;
   ConstructAndAppendGridItems(&grid_items);
 
   NGGridLayoutAlgorithmTrackCollection column_track_collection_for_min_size;
@@ -155,14 +148,9 @@
                                  &column_track_collection_for_min_size,
                                  &row_track_collection, &grid_placement);
 
-  // Create a vector of grid item indices using |NGGridChildIterator| order.
-  Vector<wtf_size_t> reordered_item_indices(grid_items.size());
-  for (wtf_size_t i = 0; i < grid_items.size(); ++i)
-    reordered_item_indices[i] = i;
-
   // Cache track span properties for grid items.
   CacheGridItemsTrackSpanProperties(column_track_collection_for_min_size,
-                                    &grid_items, &reordered_item_indices);
+                                    &grid_items);
 
   // Before the track sizing algorithm, create a copy of the column collection;
   // one will be used to compute the min size and the other for the max size.
@@ -171,12 +159,10 @@
 
   // Resolve inline size under a 'min-content' constraint.
   ComputeUsedTrackSizes(SizingConstraint::kMinContent,
-                        &column_track_collection_for_min_size, &grid_items,
-                        &reordered_item_indices);
+                        &column_track_collection_for_min_size, &grid_items);
   // Resolve inline size under a 'max-content' constraint.
   ComputeUsedTrackSizes(SizingConstraint::kMaxContent,
-                        &column_track_collection_for_max_size, &grid_items,
-                        &reordered_item_indices);
+                        &column_track_collection_for_max_size, &grid_items);
 
   const LayoutUnit grid_gap = GridGap(kForColumns);
   MinMaxSizes sizes{
@@ -351,49 +337,24 @@
   return set_indices;
 }
 
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::Iterator(
-    Vector<wtf_size_t>::const_iterator current_index,
-    Vector<GridItemData>* grid_items)
-    : current_index_(current_index), grid_items_(grid_items) {}
-
-bool NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator!=(
-    const Iterator& other) const {
-  return grid_items_ != other.grid_items_ ||
-         current_index_ != other.current_index_;
+NGGridLayoutAlgorithm::GridItems::Iterator
+NGGridLayoutAlgorithm::GridItems::begin() {
+  return Iterator(&item_data, reordered_item_indices.begin());
 }
 
-NGGridLayoutAlgorithm::GridItemData*
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator->() {
-  DCHECK_LT(*current_index_, grid_items_->size());
-  return &(grid_items_->at(*current_index_));
+NGGridLayoutAlgorithm::GridItems::Iterator
+NGGridLayoutAlgorithm::GridItems::end() {
+  return Iterator(&item_data, reordered_item_indices.end());
 }
 
-NGGridLayoutAlgorithm::GridItemData&
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator*() {
-  DCHECK_LT(*current_index_, grid_items_->size());
-  return grid_items_->at(*current_index_);
+void NGGridLayoutAlgorithm::GridItems::Append(
+    const GridItemData& new_item_data) {
+  reordered_item_indices.push_back(item_data.size());
+  item_data.emplace_back(new_item_data);
 }
 
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator&
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator++() {
-  ++current_index_;
-  return *this;
-}
-
-NGGridLayoutAlgorithm::ReorderedGridItems::ReorderedGridItems(
-    const Vector<wtf_size_t>& reordered_item_indices,
-    Vector<GridItemData>& grid_items)
-    : reordered_item_indices_(reordered_item_indices),
-      grid_items_(grid_items) {}
-
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator
-NGGridLayoutAlgorithm::ReorderedGridItems::begin() {
-  return Iterator(reordered_item_indices_.begin(), &grid_items_);
-}
-
-NGGridLayoutAlgorithm::ReorderedGridItems::Iterator
-NGGridLayoutAlgorithm::ReorderedGridItems::end() {
-  return Iterator(reordered_item_indices_.end(), &grid_items_);
+bool NGGridLayoutAlgorithm::GridItems::IsEmpty() const {
+  return item_data.IsEmpty();
 }
 
 namespace {
@@ -586,7 +547,7 @@
 }
 
 void NGGridLayoutAlgorithm::ConstructAndAppendGridItems(
-    Vector<GridItemData>* grid_items,
+    GridItems* grid_items,
     Vector<GridItemData>* out_of_flow_items) const {
   DCHECK(grid_items);
   NGGridChildIterator iterator(Node());
@@ -596,7 +557,7 @@
     // If |out_of_flow_items| is provided, store out-of-flow items separately,
     // as they do not contribute to track sizing or auto-placement.
     if (grid_item.item_type == ItemType::kInGridFlow)
-      grid_items->emplace_back(grid_item);
+      grid_items->Append(grid_item);
     else if (out_of_flow_items)
       out_of_flow_items->emplace_back(grid_item);
   }
@@ -866,7 +827,7 @@
 }
 
 void NGGridLayoutAlgorithm::BuildBlockTrackCollections(
-    Vector<GridItemData>* grid_items,
+    GridItems* grid_items,
     NGGridBlockTrackCollection* column_track_collection,
     NGGridBlockTrackCollection* row_track_collection,
     NGGridPlacement* grid_placement) const {
@@ -889,7 +850,7 @@
 }
 
 void NGGridLayoutAlgorithm::BuildAlgorithmTrackCollections(
-    Vector<GridItemData>* grid_items,
+    GridItems* grid_items,
     NGGridLayoutAlgorithmTrackCollection* column_track_collection,
     NGGridLayoutAlgorithmTrackCollection* row_track_collection,
     NGGridPlacement* grid_placement) const {
@@ -933,12 +894,12 @@
 }
 
 void NGGridLayoutAlgorithm::EnsureTrackCoverageForGridItems(
-    const Vector<GridItemData>& grid_items,
+    const GridItems& grid_items,
     NGGridBlockTrackCollection* track_collection) const {
   DCHECK(track_collection);
   const GridTrackSizingDirection track_direction =
       track_collection->Direction();
-  for (const auto& grid_item : grid_items) {
+  for (const auto& grid_item : grid_items.item_data) {
     track_collection->EnsureTrackCoverage(grid_item.StartLine(track_direction),
                                           grid_item.SpanSize(track_direction));
   }
@@ -946,18 +907,17 @@
 
 void NGGridLayoutAlgorithm::CacheGridItemsTrackSpanProperties(
     const NGGridLayoutAlgorithmTrackCollection& track_collection,
-    Vector<GridItemData>* grid_items,
-    Vector<wtf_size_t>* reordered_item_indices) const {
-  DCHECK(grid_items && reordered_item_indices);
+    GridItems* grid_items) const {
+  DCHECK(grid_items);
   const GridTrackSizingDirection track_direction = track_collection.Direction();
 
   auto CompareGridItemsByStartLine = [grid_items, track_direction](
-                                         wtf_size_t index_a,
-                                         wtf_size_t index_b) -> bool {
-    return grid_items->at(index_a).StartLine(track_direction) <
-           grid_items->at(index_b).StartLine(track_direction);
+                                         wtf_size_t a, wtf_size_t b) -> bool {
+    return grid_items->item_data[a].StartLine(track_direction) <
+           grid_items->item_data[b].StartLine(track_direction);
   };
-  std::sort(reordered_item_indices->begin(), reordered_item_indices->end(),
+  std::sort(grid_items->reordered_item_indices.begin(),
+            grid_items->reordered_item_indices.end(),
             CompareGridItemsByStartLine);
 
   auto CacheTrackSpanPropertyForAllGridItems =
@@ -967,8 +927,7 @@
         // the ranges in the track collection and the grid items, incrementally.
         auto range_iterator = track_collection.RangeIterator();
 
-        for (GridItemData& grid_item :
-             ReorderedGridItems(*reordered_item_indices, *grid_items)) {
+        for (auto& grid_item : *grid_items) {
           // We want to find the first range in the collection that:
           //   - Spans tracks located AFTER the start line of the current grid
           //   item; this can be done by checking that the last track number of
@@ -1016,8 +975,7 @@
 void NGGridLayoutAlgorithm::ComputeUsedTrackSizes(
     SizingConstraint sizing_constraint,
     NGGridLayoutAlgorithmTrackCollection* track_collection,
-    Vector<GridItemData>* grid_items,
-    Vector<wtf_size_t>* reordered_item_indices) const {
+    GridItems* grid_items) const {
   DCHECK(track_collection && grid_items);
   const GridTrackSizingDirection track_direction =
       track_collection->Direction();
@@ -1073,8 +1031,7 @@
   }
 
   // 2. Resolve intrinsic track sizing functions to absolute lengths.
-  ResolveIntrinsicTrackSizes(track_collection, grid_items,
-                             reordered_item_indices);
+  ResolveIntrinsicTrackSizes(track_collection, grid_items);
 
   // 3. If the free space is positive, distribute it equally to the base sizes
   // of all tracks, freezing tracks as they reach their growth limits (and
@@ -1474,8 +1431,8 @@
 }  // namespace
 
 void NGGridLayoutAlgorithm::IncreaseTrackSizesToAccommodateGridItems(
-    ReorderedGridItems::Iterator group_begin,
-    ReorderedGridItems::Iterator group_end,
+    GridItems::Iterator group_begin,
+    GridItems::Iterator group_end,
     const bool is_group_spanning_flex_track,
     GridItemContributionType contribution_type,
     NGGridLayoutAlgorithmTrackCollection* track_collection) const {
@@ -1568,9 +1525,8 @@
 // https://drafts.csswg.org/css-grid-2/#algo-content
 void NGGridLayoutAlgorithm::ResolveIntrinsicTrackSizes(
     NGGridLayoutAlgorithmTrackCollection* track_collection,
-    Vector<GridItemData>* grid_items,
-    Vector<wtf_size_t>* reordered_item_indices) const {
-  DCHECK(grid_items && track_collection && reordered_item_indices);
+    GridItems* grid_items) const {
+  DCHECK(track_collection && grid_items);
   const GridTrackSizingDirection track_direction =
       track_collection->Direction();
 
@@ -1581,25 +1537,23 @@
   //   not spanning a flexible track have been considered.
   //   - Finally, consider all items spanning a flexible track.
   auto CompareGridItemsForIntrinsicTrackResolution =
-      [grid_items, track_direction](wtf_size_t index_a,
-                                    wtf_size_t index_b) -> bool {
-    if (grid_items->at(index_a).IsSpanningFlexibleTrack(track_direction) ||
-        grid_items->at(index_b).IsSpanningFlexibleTrack(track_direction)) {
+      [grid_items, track_direction](wtf_size_t a, wtf_size_t b) -> bool {
+    if (grid_items->item_data[a].IsSpanningFlexibleTrack(track_direction) ||
+        grid_items->item_data[b].IsSpanningFlexibleTrack(track_direction)) {
       // Ignore span sizes if one of the items spans a track with a flexible
       // sizing function; items not spanning such tracks should come first.
-      return !grid_items->at(index_a).IsSpanningFlexibleTrack(track_direction);
+      return !grid_items->item_data[a].IsSpanningFlexibleTrack(track_direction);
     }
-    return grid_items->at(index_a).SpanSize(track_direction) <
-           grid_items->at(index_b).SpanSize(track_direction);
+    return grid_items->item_data[a].SpanSize(track_direction) <
+           grid_items->item_data[b].SpanSize(track_direction);
   };
-  std::sort(reordered_item_indices->begin(), reordered_item_indices->end(),
+  std::sort(grid_items->reordered_item_indices.begin(),
+            grid_items->reordered_item_indices.end(),
             CompareGridItemsForIntrinsicTrackResolution);
-  auto reordered_items =
-      ReorderedGridItems(*reordered_item_indices, *grid_items);
 
   // First, process the items that don't span a flexible track.
-  auto current_group_begin = reordered_items.begin();
-  while (current_group_begin != reordered_items.end() &&
+  auto current_group_begin = grid_items->begin();
+  while (current_group_begin != grid_items->end() &&
          !current_group_begin->IsSpanningFlexibleTrack(track_direction)) {
     // Each iteration considers all items with the same span size.
     wtf_size_t current_group_span_size =
@@ -1609,7 +1563,7 @@
     do {
       DCHECK(!current_group_end->IsSpanningFlexibleTrack(track_direction));
       ++current_group_end;
-    } while (current_group_end != reordered_items.end() &&
+    } while (current_group_end != grid_items->end() &&
              !current_group_end->IsSpanningFlexibleTrack(track_direction) &&
              current_group_end->SpanSize(track_direction) ==
                  current_group_span_size);
@@ -1646,24 +1600,24 @@
   //   sizing function...
 #if DCHECK_IS_ON()
   // Every grid item of the remaining group should span a flexible track.
-  for (auto it = current_group_begin; it != reordered_items.end(); ++it)
+  for (auto it = current_group_begin; it != grid_items->end(); ++it)
     DCHECK(it->IsSpanningFlexibleTrack(track_direction));
 #endif
 
   // Now, process items spanning flexible tracks (if any).
-  if (current_group_begin != reordered_items.end()) {
+  if (current_group_begin != grid_items->end()) {
     // We can safely skip contributions for maximums since a <flex> definition
     // does not have an intrinsic max track sizing function.
     IncreaseTrackSizesToAccommodateGridItems(
-        current_group_begin, reordered_items.end(),
+        current_group_begin, grid_items->end(),
         /* is_group_spanning_flex_track */ true,
         GridItemContributionType::kForIntrinsicMinimums, track_collection);
     IncreaseTrackSizesToAccommodateGridItems(
-        current_group_begin, reordered_items.end(),
+        current_group_begin, grid_items->end(),
         /* is_group_spanning_flex_track */ true,
         GridItemContributionType::kForContentBasedMinimums, track_collection);
     IncreaseTrackSizesToAccommodateGridItems(
-        current_group_begin, reordered_items.end(),
+        current_group_begin, grid_items->end(),
         /* is_group_spanning_flex_track */ true,
         GridItemContributionType::kForMaxContentMinimums, track_collection);
   }
@@ -1688,7 +1642,7 @@
     return;
 
   GridSetVector sets_to_grow;
-  sets_to_grow.ReserveCapacity(track_collection->SetCount());
+  sets_to_grow.ReserveInitialCapacity(track_collection->SetCount());
   for (auto set_iterator = track_collection->GetSetIterator();
        !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
     sets_to_grow.push_back(&set_iterator.CurrentSet());
@@ -1928,10 +1882,11 @@
                                           BorderScrollbarPadding().block_start,
                                           GridGap(kForRows, available_size));
 
-  LayoutUnit set_offset = track_alignment_geometry.start_offset;
   Vector<LayoutUnit> offsets;
-  offsets.ReserveCapacity(track_collection.SetCount() + 1);
+  offsets.ReserveInitialCapacity(track_collection.SetCount() + 1);
+  LayoutUnit set_offset = track_alignment_geometry.start_offset;
   offsets.push_back(set_offset);
+
   for (auto set_iterator = track_collection.GetSetIterator();
        !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
     const auto& set = set_iterator.CurrentSet();
@@ -2052,7 +2007,7 @@
 }  // namespace
 
 void NGGridLayoutAlgorithm::PlaceGridItems(
-    const Vector<GridItemData>& grid_items,
+    const GridItems& grid_items,
     const SetGeometry& column_set_geometry,
     const SetGeometry& row_set_geometry,
     LayoutUnit block_size) {
@@ -2070,7 +2025,7 @@
   base::Optional<PositionAndBaseline> alignment_baseline;
   base::Optional<PositionAndBaseline> fallback_baseline;
 
-  for (const GridItemData& grid_item : grid_items) {
+  for (const auto& grid_item : grid_items.item_data) {
     DCHECK(grid_item.column_set_indices.has_value());
     DCHECK(grid_item.row_set_indices.has_value());
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index a3c13e4b..15a8b18 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -97,6 +97,56 @@
     base::Optional<ItemSetIndices> row_set_indices;
   };
 
+  struct GridItems {
+    class Iterator
+        : public std::iterator<std::input_iterator_tag, GridItemData> {
+     public:
+      Iterator(Vector<GridItemData>* item_data,
+               Vector<wtf_size_t>::const_iterator current_index)
+          : item_data_(item_data), current_index_(current_index) {
+        DCHECK(item_data_);
+      }
+
+      bool operator!=(const Iterator& other) const {
+        return current_index_ != other.current_index_ ||
+               item_data_ != other.item_data_;
+      }
+
+      Iterator& operator++() {
+        ++current_index_;
+        return *this;
+      }
+
+      GridItemData* operator->() {
+        DCHECK(current_index_ && *current_index_ < item_data_->size());
+        return &(item_data_->at(*current_index_));
+      }
+
+      GridItemData& operator*() {
+        DCHECK(current_index_ && *current_index_ < item_data_->size());
+        return item_data_->at(*current_index_);
+      }
+
+     private:
+      Vector<GridItemData>* item_data_;
+      Vector<wtf_size_t>::const_iterator current_index_;
+    };
+
+    Iterator begin();
+    Iterator end();
+
+    void Append(const GridItemData& new_item_data);
+
+    bool IsEmpty() const;
+
+    // Grid items are appended to |item_data_| in the same order provided by
+    // |NGGridChildIterator|, which iterates over its children in order-modified
+    // document order; we want to keep such order since auto-placement and
+    // painting order rely on it later in the algorithm.
+    Vector<GridItemData> item_data;
+    Vector<wtf_size_t> reordered_item_indices;
+  };
+
   explicit NGGridLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
 
   scoped_refptr<const NGLayoutResult> Layout() override;
@@ -107,34 +157,6 @@
 
   enum class SizingConstraint { kLayout, kMinContent, kMaxContent };
 
-  class ReorderedGridItems {
-   public:
-    class Iterator
-        : public std::iterator<std::input_iterator_tag, GridItemData> {
-     public:
-      Iterator(Vector<wtf_size_t>::const_iterator current_index,
-               Vector<GridItemData>* grid_items);
-
-      bool operator!=(const Iterator& other) const;
-      GridItemData* operator->();
-      GridItemData& operator*();
-      Iterator& operator++();
-
-     private:
-      Vector<wtf_size_t>::const_iterator current_index_;
-      Vector<GridItemData>* grid_items_;
-    };
-
-    ReorderedGridItems(const Vector<wtf_size_t>& reordered_item_indices,
-                       Vector<GridItemData>& grid_items);
-    Iterator begin();
-    Iterator end();
-
-   private:
-    const Vector<wtf_size_t>& reordered_item_indices_;
-    Vector<GridItemData>& grid_items_;
-  };
-
   // Returns the size that a grid item will distribute across the tracks with an
   // intrinsic sizing function it spans in the relevant track direction.
   LayoutUnit ContributionSizeForGridItem(
@@ -146,18 +168,18 @@
       GridTrackSizingDirection track_direction) const;
 
   void ConstructAndAppendGridItems(
-      Vector<GridItemData>* grid_items,
+      GridItems* grid_items,
       Vector<GridItemData>* out_of_flow_items = nullptr) const;
   GridItemData MeasureGridItem(const NGBlockNode node) const;
 
   void BuildBlockTrackCollections(
-      Vector<GridItemData>* grid_items,
+      GridItems* grid_items,
       NGGridBlockTrackCollection* column_track_collection,
       NGGridBlockTrackCollection* row_track_collection,
       NGGridPlacement* grid_placement) const;
 
   void BuildAlgorithmTrackCollections(
-      Vector<GridItemData>* grid_items,
+      GridItems* grid_items,
       NGGridLayoutAlgorithmTrackCollection* column_track_collection,
       NGGridLayoutAlgorithmTrackCollection* row_track_collection,
       NGGridPlacement* grid_placement) const;
@@ -167,33 +189,30 @@
                           NGGridBlockTrackCollection* track_collection) const;
   // Ensure coverage in block collection after grid items have been placed.
   void EnsureTrackCoverageForGridItems(
-      const Vector<GridItemData>& grid_items,
+      const GridItems& grid_items,
       NGGridBlockTrackCollection* track_collection) const;
 
   // For every grid item, caches properties of the track sizing functions it
   // spans (i.e. whether an item spans intrinsic or flexible tracks).
   void CacheGridItemsTrackSpanProperties(
       const NGGridLayoutAlgorithmTrackCollection& track_collection,
-      Vector<GridItemData>* grid_items,
-      Vector<wtf_size_t>* reordered_item_indices) const;
+      GridItems* grid_items) const;
 
   // Calculates from the min and max track sizing functions the used track size.
   void ComputeUsedTrackSizes(
       SizingConstraint sizing_constraint,
       NGGridLayoutAlgorithmTrackCollection* track_collection,
-      Vector<GridItemData>* grid_items,
-      Vector<wtf_size_t>* reordered_item_indices) const;
+      GridItems* grid_items) const;
 
   // These methods implement the steps of the algorithm for intrinsic track size
   // resolution defined in https://drafts.csswg.org/css-grid-2/#algo-content.
   void ResolveIntrinsicTrackSizes(
       NGGridLayoutAlgorithmTrackCollection* track_collection,
-      Vector<GridItemData>* grid_items,
-      Vector<wtf_size_t>* reordered_item_indices) const;
+      GridItems* grid_items) const;
 
   void IncreaseTrackSizesToAccommodateGridItems(
-      ReorderedGridItems::Iterator group_begin,
-      ReorderedGridItems::Iterator group_end,
+      GridItems::Iterator group_begin,
+      GridItems::Iterator group_end,
       const bool is_group_spanning_flex_track,
       GridItemContributionType contribution_type,
       NGGridLayoutAlgorithmTrackCollection* track_collection) const;
@@ -225,7 +244,7 @@
       const NGGridLayoutAlgorithmTrackCollection& track_collection) const;
 
   // Layout the |grid_items| based on the offsets provided.
-  void PlaceGridItems(const Vector<GridItemData>& grid_items,
+  void PlaceGridItems(const GridItems& grid_items,
                       const SetGeometry& column_set_geometry,
                       const SetGeometry& row_set_geometry,
                       LayoutUnit block_size);
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
index 9d7475fd..575d2b9 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
@@ -54,25 +54,20 @@
         &grid_items_, &column_track_collection_, &row_track_collection_,
         &grid_placement);
 
-    // Create a vector of grid item indices using |NGGridChildIterator| order.
-    Vector<wtf_size_t> reordered_item_indices(grid_items_.size());
-    for (wtf_size_t i = 0; i < grid_items_.size(); ++i)
-      reordered_item_indices[i] = i;
-
     // Cache track span properties for grid items.
-    algorithm.CacheGridItemsTrackSpanProperties(
-        column_track_collection_, &grid_items_, &reordered_item_indices);
-    algorithm.CacheGridItemsTrackSpanProperties(
-        row_track_collection_, &grid_items_, &reordered_item_indices);
+    algorithm.CacheGridItemsTrackSpanProperties(column_track_collection_,
+                                                &grid_items_);
+    algorithm.CacheGridItemsTrackSpanProperties(row_track_collection_,
+                                                &grid_items_);
 
     // Resolve inline size.
     algorithm.ComputeUsedTrackSizes(
         NGGridLayoutAlgorithm::SizingConstraint::kLayout,
-        &column_track_collection_, &grid_items_, &reordered_item_indices);
+        &column_track_collection_, &grid_items_);
     // Resolve block size.
     algorithm.ComputeUsedTrackSizes(
         NGGridLayoutAlgorithm::SizingConstraint::kLayout,
-        &row_track_collection_, &grid_items_, &reordered_item_indices);
+        &row_track_collection_, &grid_items_);
   }
 
   NGGridLayoutAlgorithmTrackCollection& TrackCollection(
@@ -83,24 +78,20 @@
 
   // Helper methods to access private data on NGGridLayoutAlgorithm. This class
   // is a friend of NGGridLayoutAlgorithm but the individual tests are not.
-  wtf_size_t GridItemCount(const NGGridLayoutAlgorithm& algorithm) {
-    return grid_items_.size();
-  }
+  wtf_size_t GridItemCount() { return grid_items_.item_data.size(); }
 
   Vector<LayoutUnit> GridItemInlineMarginSum(
       const NGGridLayoutAlgorithm& algorithm) {
     Vector<LayoutUnit> results;
-    for (const auto& item : grid_items_) {
+    for (const auto& item : grid_items_.item_data)
       results.push_back(item.margins.InlineSum());
-    }
     return results;
   }
 
   Vector<GridArea> GridItemGridAreas(const NGGridLayoutAlgorithm& algorithm) {
     Vector<GridArea> results;
-    for (const auto& item : grid_items_) {
+    for (const auto& item : grid_items_.item_data)
       results.push_back(item.resolved_position);
-    }
     return results;
   }
 
@@ -108,8 +99,8 @@
       const NGGridLayoutAlgorithm& algorithm,
       TrackSpanProperties::PropertyId property) {
     Vector<wtf_size_t> results;
-    for (wtf_size_t i = 0; i < grid_items_.size(); ++i) {
-      if (grid_items_[i].column_span_properties.HasProperty(property))
+    for (wtf_size_t i = 0; i < GridItemCount(); ++i) {
+      if (grid_items_.item_data[i].column_span_properties.HasProperty(property))
         results.push_back(i);
     }
     return results;
@@ -119,8 +110,8 @@
       const NGGridLayoutAlgorithm& algorithm,
       TrackSpanProperties::PropertyId property) {
     Vector<wtf_size_t> results;
-    for (wtf_size_t i = 0; i < grid_items_.size(); ++i) {
-      if (grid_items_[i].row_span_properties.HasProperty(property))
+    for (wtf_size_t i = 0; i < GridItemCount(); ++i) {
+      if (grid_items_.item_data[i].row_span_properties.HasProperty(property))
         results.push_back(i);
     }
     return results;
@@ -175,7 +166,7 @@
     return fragment->DumpFragmentTree(flags);
   }
 
-  Vector<NGGridLayoutAlgorithm::GridItemData> grid_items_;
+  NGGridLayoutAlgorithm::GridItems grid_items_;
   Vector<NGGridLayoutAlgorithm::GridItemData> out_of_flow_items_;
 
   NGGridLayoutAlgorithmTrackCollection column_track_collection_;
@@ -321,20 +312,20 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 9U);
+  EXPECT_EQ(GridItemCount(), 9U);
 
   Vector<LayoutUnit> actual_inline_margin_sums =
       GridItemInlineMarginSum(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), actual_inline_margin_sums.size());
+  EXPECT_EQ(GridItemCount(), actual_inline_margin_sums.size());
 
   LayoutUnit expected_inline_margin_sums[] = {
       LayoutUnit(100), LayoutUnit(30),  LayoutUnit(0),
       LayoutUnit(0),   LayoutUnit(0),   LayoutUnit(0),
       LayoutUnit(10),  LayoutUnit(100), LayoutUnit(0)};
 
-  for (size_t i = 0; i < GridItemCount(algorithm); ++i) {
+  for (size_t i = 0; i < GridItemCount(); ++i) {
     EXPECT_EQ(actual_inline_margin_sums[i], expected_inline_margin_sums[i])
         << " index: " << i;
   }
@@ -372,9 +363,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
       &TrackCollection(kForRows), 0u);
@@ -429,9 +420,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
       &TrackCollection(kForRows), 0u);
@@ -517,9 +508,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
       &TrackCollection(kForColumns), 0u);
@@ -588,9 +579,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
       &TrackCollection(kForColumns), 0u);
@@ -655,9 +646,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
       &TrackCollection(kForColumns), 0u);
@@ -714,9 +705,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 5U);
+  EXPECT_EQ(GridItemCount(), 5U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
       &TrackCollection(kForColumns), 0u);
@@ -802,9 +793,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 4U);
+  EXPECT_EQ(GridItemCount(), 4U);
 
   Vector<GridArea> grid_positions = GridItemGridAreas(algorithm);
   ASSERT_EQ(grid_positions.size(), 4U);
@@ -931,9 +922,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 16U);
+  EXPECT_EQ(GridItemCount(), 16U);
 
   Vector<GridArea> grid_positions = GridItemGridAreas(algorithm);
   ASSERT_EQ(grid_positions.size(), 16U);
@@ -1016,9 +1007,9 @@
       CalculateInitialFragmentGeometry(space, node);
 
   NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
-  EXPECT_EQ(GridItemCount(algorithm), 0U);
+  EXPECT_EQ(GridItemCount(), 0U);
   BuildGridItemsAndTrackCollections(algorithm);
-  EXPECT_EQ(GridItemCount(algorithm), 3U);
+  EXPECT_EQ(GridItemCount(), 3U);
 
   NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
       &TrackCollection(kForColumns), 0u);
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
index b109667c0..561e4cd4 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
@@ -30,8 +30,7 @@
       row_auto_repetitions_(row_auto_repetitions) {}
 
 // https://drafts.csswg.org/css-grid/#auto-placement-algo
-void NGGridPlacement::RunAutoPlacementAlgorithm(
-    Vector<GridItemData>* grid_items) {
+void NGGridPlacement::RunAutoPlacementAlgorithm(GridItems* grid_items) {
   DCHECK(grid_items);
   minor_max_end_line_ = (minor_direction_ == kForColumns)
                             ? GridPositionsResolver::ExplicitGridColumnCount(
@@ -57,7 +56,7 @@
 
   // Step 4. Position remaining grid items.
   AutoPlacementCursor placement_cursor;
-  for (GridItemData& grid_item : *grid_items) {
+  for (GridItemData& grid_item : grid_items->item_data) {
     switch (grid_item.AutoPlacement(major_direction_)) {
       case AutoPlacementType::kBoth:
         PlaceAutoBothAxisGridItem(&grid_item, &placement_cursor, *grid_items);
@@ -75,11 +74,11 @@
 }
 
 wtf_size_t NGGridPlacement::DetermineTrackStartOffset(
-    const Vector<GridItemData>& grid_items,
+    const GridItems& grid_items,
     GridTrackSizingDirection track_direction) const {
   wtf_size_t track_start_offset = 0;
 
-  for (const GridItemData& grid_item : grid_items) {
+  for (const auto& grid_item : grid_items.item_data) {
     GridSpan grid_item_span =
         GridPositionsResolver::ResolveGridPositionsFromStyle(
             grid_style_, grid_item.node.Style(), track_direction,
@@ -94,11 +93,11 @@
   return track_start_offset;
 }
 
-bool NGGridPlacement::PlaceNonAutoGridItems(Vector<GridItemData>* grid_items) {
+bool NGGridPlacement::PlaceNonAutoGridItems(GridItems* grid_items) {
   DCHECK(grid_items);
   bool has_auto_placed_items = false;
 
-  for (GridItemData& grid_item : *grid_items) {
+  for (GridItemData& grid_item : grid_items->item_data) {
     bool has_definite_major_placement =
         PlaceGridItem(&grid_item, major_direction_);
     bool has_definite_minor_placement =
@@ -118,8 +117,7 @@
   return has_auto_placed_items;
 }
 
-void NGGridPlacement::PlaceGridItemsLockedToMajorAxis(
-    Vector<GridItemData>* grid_items) {
+void NGGridPlacement::PlaceGridItemsLockedToMajorAxis(GridItems* grid_items) {
   DCHECK(grid_items);
 
   // Mapping between the major axis tracks and the last auto-placed item's end
@@ -128,7 +126,7 @@
   // See https://drafts.csswg.org/css-grid/#auto-placement-algo.
   HashMap<wtf_size_t, wtf_size_t> minor_cursors;
 
-  for (GridItemData& grid_item : *grid_items) {
+  for (GridItemData& grid_item : grid_items->item_data) {
     // Only consider grid items that require minor axis auto-placement.
     if (grid_item.AutoPlacement(major_direction_) != AutoPlacementType::kMinor)
       continue;
@@ -165,7 +163,7 @@
 void NGGridPlacement::PlaceAutoMajorAxisGridItem(
     GridItemData* grid_item,
     AutoPlacementCursor* placement_cursor,
-    const Vector<GridItemData>& grid_items) {
+    const GridItems& grid_items) {
   DCHECK(grid_item);
 
   if (HasSparsePacking()) {
@@ -205,7 +203,7 @@
 void NGGridPlacement::PlaceAutoBothAxisGridItem(
     GridItemData* grid_item,
     AutoPlacementCursor* placement_cursor,
-    const Vector<GridItemData>& grid_items) {
+    const GridItems& grid_items) {
   DCHECK(grid_item);
 
   // For dense packing, set the cursor’s major and minor positions to the
@@ -269,17 +267,16 @@
   return true;
 }
 
-bool NGGridPlacement::DoesItemOverlap(
-    wtf_size_t major_start,
-    wtf_size_t major_end,
-    wtf_size_t minor_start,
-    wtf_size_t minor_end,
-    const Vector<GridItemData>& grid_items) const {
+bool NGGridPlacement::DoesItemOverlap(wtf_size_t major_start,
+                                      wtf_size_t major_end,
+                                      wtf_size_t minor_start,
+                                      wtf_size_t minor_end,
+                                      const GridItems& grid_items) const {
   DCHECK_LE(major_start, major_end);
   DCHECK_LE(minor_start, minor_end);
   // TODO(janewman): Implement smarter collision detection, iterating over all
   // items is not ideal and has large performance implications.
-  for (const GridItemData& grid_item : grid_items) {
+  for (const auto& grid_item : grid_items.item_data) {
     if (grid_item.Span(major_direction_).IsIndefinite())
       continue;
     // Only test against definite positions.
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h
index c98aa392..b2d331c3 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h
@@ -20,6 +20,7 @@
  public:
   enum class PackingBehavior { kSparse, kDense };
 
+  using GridItems = NGGridLayoutAlgorithm::GridItems;
   using GridItemData = NGGridLayoutAlgorithm::GridItemData;
   using AutoPlacementType = NGGridLayoutAlgorithm::AutoPlacementType;
 
@@ -27,7 +28,7 @@
                   const wtf_size_t column_auto_repetitions,
                   const wtf_size_t row_auto_repetitions);
 
-  void RunAutoPlacementAlgorithm(Vector<GridItemData>* grid_items);
+  void RunAutoPlacementAlgorithm(GridItems* grid_items);
   // Helper function to resolve start and end lines of out of flow items.
   void ResolveOutOfFlowItemGridLines(
       const NGGridLayoutAlgorithmTrackCollection& track_collection,
@@ -46,24 +47,24 @@
   // Compute the track start offset from the grid items positioned at negative
   // indices.
   wtf_size_t DetermineTrackStartOffset(
-      const Vector<GridItemData>& grid_items,
+      const GridItems& grid_items,
       GridTrackSizingDirection track_direction) const;
 
   // Place non auto-positioned elements from |grid_items|; returns true if any
   // item needs to resolve an automatic position. Otherwise, false.
-  bool PlaceNonAutoGridItems(Vector<GridItemData>* grid_items);
+  bool PlaceNonAutoGridItems(GridItems* grid_items);
   // Place elements from |grid_items| that have a definite position on the major
   // axis but need auto-placement on the minor axis.
-  void PlaceGridItemsLockedToMajorAxis(Vector<GridItemData>* grid_items);
+  void PlaceGridItemsLockedToMajorAxis(GridItems* grid_items);
   // Place an item that has a definite position on the minor axis but need
   // auto-placement on the major axis.
   void PlaceAutoMajorAxisGridItem(GridItemData* grid_item,
                                   AutoPlacementCursor* placement_cursor,
-                                  const Vector<GridItemData>& grid_items);
+                                  const GridItems& grid_items);
   // Place an item that needs auto-placement on both the major and minor axis.
   void PlaceAutoBothAxisGridItem(GridItemData* grid_item,
                                  AutoPlacementCursor* placement_cursor,
-                                 const Vector<GridItemData>& grid_items);
+                                 const GridItems& grid_items);
 
   // Places a grid item; returns true if it has a definite position in the given
   // direction, false if the item needs auto-placement.
@@ -75,7 +76,7 @@
                        wtf_size_t major_end,
                        wtf_size_t minor_start,
                        wtf_size_t minor_end,
-                       const Vector<GridItemData>& grid_items) const;
+                       const GridItems& grid_items) const;
 
   wtf_size_t StartOffset(GridTrackSizingDirection track_direction) const;
   wtf_size_t AutoRepeatTrackCount(
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
index 50ae6a13..16b85d4 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
@@ -300,8 +300,8 @@
     wtf_size_t end_set_index_;
   };
 
-  using SetIterator = SetIteratorBase<false>;
-  using ConstSetIterator = SetIteratorBase<true>;
+  typedef SetIteratorBase<false> SetIterator;
+  typedef SetIteratorBase<true> ConstSetIterator;
 
   NGGridLayoutAlgorithmTrackCollection() = default;
   // |is_content_box_size_indefinite| is used to normalize percentage track
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
index c4474e9..41301a3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
@@ -369,6 +369,28 @@
   return LayoutRectOutsets();
 }
 
+// Effective column index is index of columns with mergeable
+// columns skipped. Used in a11y.
+unsigned LayoutNGTable::AbsoluteColumnToEffectiveColumn(
+    unsigned absolute_column_index) const {
+  NOT_DESTROYED();
+  if (!cached_table_columns_) {
+    NOTREACHED();
+    return absolute_column_index;
+  }
+  unsigned effective_column_index = 0;
+  unsigned column_count = cached_table_columns_.get()->data.size();
+  for (unsigned current_column_index = 0; current_column_index < column_count;
+       ++current_column_index) {
+    if (current_column_index != 0 &&
+        !cached_table_columns_.get()->data[current_column_index].is_mergeable)
+      ++effective_column_index;
+    if (current_column_index == absolute_column_index)
+      return effective_column_index;
+  }
+  return effective_column_index;
+}
+
 bool LayoutNGTable::IsFirstCell(const LayoutNGTableCellInterface& cell) const {
   NOT_DESTROYED();
   const LayoutNGTableRowInterface* row = cell.RowInterface();
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
index 5c70f14..c3eb81b 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
@@ -172,14 +172,8 @@
     return ShouldCollapseBorders() ? 0 : StyleRef().VerticalBorderSpacing();
   }
 
-  // Legacy had a concept of colspan column compression. This is a legacy
-  // method to map between absolute and compressed columns.
-  // Because NG does not compress columns, absolute and effective are the same.
   unsigned AbsoluteColumnToEffectiveColumn(
-      unsigned absolute_column_index) const final {
-    NOT_DESTROYED();
-    return absolute_column_index;
-  }
+      unsigned absolute_column_index) const final;
 
   // NG does not need this method. Sections are not cached.
   void RecalcSectionsIfNeeded() const final {}
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
index b94c476..e675fd2 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
@@ -132,7 +132,12 @@
 // behaviour is correct. Consider removing these methods.
 unsigned LayoutNGTableSection::NumEffectiveColumns() const {
   NOT_DESTROYED();
-  return To<LayoutNGTable>(TableInterface()->ToLayoutObject())->ColumnCount();
+  const LayoutNGTable* table = Table();
+  DCHECK(table);
+  wtf_size_t column_count = table->ColumnCount();
+  if (column_count == 0)
+    return 0;
+  return table->AbsoluteColumnToEffectiveColumn(column_count - 1) + 1;
 }
 
 // TODO(crbug.com/1079133): Used by AXLayoutObject::IsDataTable/ColumnCount,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 768b3733..4621a92cc 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -55,9 +55,17 @@
     const NGTableTypes::Columns& column_constraints,
     LayoutUnit inline_table_border_padding,
     LayoutUnit inline_border_spacing) {
-  unsigned inline_space_count = 2 + (column_constraints.data.size() > 1
-                                         ? column_constraints.data.size() - 1
-                                         : 0);
+  unsigned inline_space_count = 2;
+  bool is_first_column = true;
+  for (const NGTableTypes::Column& column : column_constraints.data) {
+    if (!column.is_mergeable) {
+      if (is_first_column)
+        is_first_column = false;
+      else
+        inline_space_count++;
+    }
+  }
+
   return inline_table_border_padding +
          inline_space_count * inline_border_spacing;
 }
@@ -146,7 +154,13 @@
     *has_collapsed_columns =
         *has_collapsed_columns || column_constraint.is_collapsed;
     column_location.offset = column_offset;
-    if (shrink_collapsed && column_constraint.is_collapsed) {
+    if (column_constraints.data[i].is_mergeable &&
+        (column_sizes[i] == kIndefiniteSize ||
+         column_sizes[i] == LayoutUnit())) {
+      // Empty mergeable columns are treated as collapsed.
+      column_location.size = LayoutUnit();
+      column_location.is_collapsed = true;
+    } else if (shrink_collapsed && column_constraint.is_collapsed) {
       column_location.is_collapsed = true;
       column_location.size = LayoutUnit();
     } else {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index 98b84d70..d7e735e 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -22,7 +22,6 @@
   unsigned percent_columns_count = 0;
   unsigned fixed_columns_count = 0;
   unsigned auto_columns_count = 0;
-
   // What guesses mean is described in table specification.
   // https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm
   enum { kMinGuess, kPercentageGuess, kSpecifiedGuess, kMaxGuess, kAboveMax };
@@ -38,7 +37,10 @@
     all_columns_count++;
     DCHECK(column->min_inline_size);
     DCHECK(column->max_inline_size);
-    if (column->percent) {
+
+    if (column->is_mergeable) {
+      ;  // Mergeable columns are ignored.
+    } else if (column->percent) {
       percent_columns_count++;
       total_percent += *column->percent;
       LayoutUnit percent_inline_size =
@@ -92,6 +94,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         *computed_size = column->min_inline_size.value_or(LayoutUnit());
       }
     } break;
@@ -106,6 +110,8 @@
       LayoutUnit* last_computed_size = nullptr;
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           last_computed_size = computed_size;
           LayoutUnit percent_inline_size =
@@ -144,6 +150,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           *computed_size = column->ResolvePercentInlineSize(target_inline_size);
         } else if (column->is_constrained) {
@@ -190,6 +198,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           *computed_size = column->ResolvePercentInlineSize(target_inline_size);
         } else if (column->is_constrained || is_exact_match) {
@@ -226,6 +236,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           if (column->percent) {
             *computed_size =
                 column->ResolvePercentInlineSize(target_inline_size);
@@ -256,6 +268,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           if (column->percent) {
             *computed_size =
                 column->ResolvePercentInlineSize(target_inline_size);
@@ -286,6 +300,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           DCHECK(column->percent);
           last_computed_size = computed_size;
           if (total_percent > 0.0f) {
@@ -447,18 +463,32 @@
   NGTableTypes::Column* end_column = start_column + colspan_cell.span;
   DCHECK_NE(start_column, end_column);
 
+  // Inline sizes for redistribution exclude border spacing.
+  LayoutUnit total_inner_border_spacing;
+  unsigned effective_span = 0;
+  bool is_first_column = true;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (column->is_mergeable)
+      continue;
+    ++effective_span;
+    if (!is_first_column)
+      total_inner_border_spacing += inline_border_spacing;
+    else
+      is_first_column = false;
+  }
   LayoutUnit colspan_cell_min_inline_size;
   LayoutUnit colspan_cell_max_inline_size;
   // Colspanned cells only distribute min inline size if constrained.
   if (colspan_cell.cell_inline_constraint.is_constrained) {
     colspan_cell_min_inline_size =
         (colspan_cell.cell_inline_constraint.min_inline_size -
-         (colspan_cell.span - 1) * inline_border_spacing)
+         total_inner_border_spacing)
             .ClampNegativeToZero();
   }
   colspan_cell_max_inline_size =
       (colspan_cell.cell_inline_constraint.max_inline_size -
-       (colspan_cell.span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
 
   // Distribute min/max/percentage evenly between all cells.
@@ -468,18 +498,19 @@
       colspan_cell.cell_inline_constraint.percent.value_or(0.0f);
 
   LayoutUnit new_min_size = LayoutUnit(colspan_cell_min_inline_size /
-                                       static_cast<float>(colspan_cell.span));
+                                       static_cast<float>(effective_span));
   LayoutUnit new_max_size = LayoutUnit(colspan_cell_max_inline_size /
-                                       static_cast<float>(colspan_cell.span));
+                                       static_cast<float>(effective_span));
   base::Optional<float> new_percent;
   if (colspan_cell.cell_inline_constraint.percent) {
-    new_percent =
-        *colspan_cell.cell_inline_constraint.percent / colspan_cell.span;
+    new_percent = *colspan_cell.cell_inline_constraint.percent / effective_span;
   }
 
   NGTableTypes::Column* last_column;
   for (NGTableTypes::Column* column = start_column; column < end_column;
        ++column) {
+    if (column->is_mergeable)
+      continue;
     last_column = column;
     rounding_error_min_inline_size -= new_min_size;
     rounding_error_max_inline_size -= new_max_size;
@@ -521,13 +552,25 @@
   NGTableTypes::Column* end_column = start_column + effective_span;
 
   // Inline sizes for redistribution exclude border spacing.
+  LayoutUnit total_inner_border_spacing;
+  bool is_first_column = true;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (!column->is_mergeable) {
+      if (!is_first_column)
+        total_inner_border_spacing += inline_border_spacing;
+      else
+        is_first_column = false;
+    }
+  }
+
   LayoutUnit colspan_cell_min_inline_size =
       (colspan_cell.cell_inline_constraint.min_inline_size -
-       (effective_span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
   LayoutUnit colspan_cell_max_inline_size =
       (colspan_cell.cell_inline_constraint.max_inline_size -
-       (effective_span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
   base::Optional<float> colspan_cell_percent =
       colspan_cell.cell_inline_constraint.percent;
@@ -544,6 +587,8 @@
         column->max_inline_size = LayoutUnit();
       if (!column->min_inline_size)
         column->min_inline_size = LayoutUnit();
+      if (column->is_mergeable)
+        continue;
       all_columns_count++;
       if (column->percent) {
         percent_columns_count++;
@@ -559,7 +604,7 @@
       // max_inline_size.
       for (NGTableTypes::Column* column = start_column; column != end_column;
            ++column) {
-        if (column->percent)
+        if (column->percent || column->is_mergeable)
           continue;
         float column_percent;
         if (nonpercent_columns_max_inline_size != LayoutUnit()) {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
index ac8f540..cc9c95a17 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
@@ -21,7 +21,9 @@
                                 percent,
                                 /* border_padding */ LayoutUnit(),
                                 is_constrained,
-                                /* is_collapsed */ false};
+                                /* is_collapsed */ false,
+                                /* is_table_fixed */ false,
+                                /* is_mergeable */ false};
   }
 
   NGTableTypes::Row MakeRow(int block_size,
@@ -155,16 +157,16 @@
   column_constraints->data.Shrink(0);
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(0), column_widths[0], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(3.33333), column_widths[1], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(3.33333), column_widths[2], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(0), column_widths[3], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
 
   LayoutUnit assignable_table_inline_size =
       column_widths[0] + column_widths[1] + column_widths[2] + column_widths[3];
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index a3a0b4b..bf9f8b0d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -85,6 +85,7 @@
   InlineSizesFromStyle(style, /* inline_border_padding */ LayoutUnit(),
                        /* is_parallel */ true, &inline_size, &min_inline_size,
                        &max_inline_size, &percentage_inline_size);
+  bool is_mergeable;
   if (!inline_size)
     inline_size = default_inline_size;
   if (min_inline_size && inline_size)
@@ -93,13 +94,16 @@
   if (percentage_inline_size && *percentage_inline_size == 0.0f)
     percentage_inline_size.reset();
   bool is_collapsed = style.Visibility() == EVisibility::kCollapse;
-  return Column{min_inline_size.value_or(LayoutUnit()),
-                inline_size,
+  if (is_table_fixed) {
+    is_mergeable = false;
+  } else {
+    is_mergeable = (inline_size.value_or(LayoutUnit()) == LayoutUnit()) &&
+                   (percentage_inline_size.value_or(0.0f) == 0.0f);
+  }
+  return Column(min_inline_size.value_or(LayoutUnit()), inline_size,
                 percentage_inline_size,
-                LayoutUnit() /* percent_border_padding */,
-                is_constrained,
-                is_collapsed,
-                is_table_fixed};
+                LayoutUnit() /* percent_border_padding */, is_constrained,
+                is_collapsed, is_table_fixed, is_mergeable);
 }
 
 // Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures
@@ -286,7 +290,8 @@
   // Constrained columns in fixed tables take precedence over cells.
   if (is_constrained && is_table_fixed)
     return;
-
+  if (!is_table_fixed)
+    is_mergeable = false;
   if (min_inline_size) {
     if (min_inline_size < cell->min_inline_size) {
       min_inline_size = cell->min_inline_size;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index 47e7177..b892c3f2 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -57,6 +57,23 @@
   // Constraint for a column.
   struct Column {
     DISALLOW_NEW();
+    Column(const base::Optional<LayoutUnit>& min_inline_size,
+           const base::Optional<LayoutUnit>& max_inline_size,
+           const base::Optional<float>& percent,
+           LayoutUnit percent_border_padding,
+           bool is_constrained,
+           bool is_collapsed,
+           bool is_table_fixed,
+           bool is_mergeable)
+        : min_inline_size(min_inline_size),
+          max_inline_size(max_inline_size),
+          percent(percent),
+          percent_border_padding(percent_border_padding),
+          is_constrained(is_constrained),
+          is_collapsed(is_collapsed),
+          is_table_fixed(is_table_fixed),
+          is_mergeable(is_mergeable) {}
+    Column() = default;
     // These members are initialized from <col> and <colgroup>, then they
     // accumulate data from |CellInlineConstraint|s.
     base::Optional<LayoutUnit> min_inline_size;
@@ -68,6 +85,7 @@
     bool is_constrained = false;
     bool is_collapsed = false;
     bool is_table_fixed = false;
+    bool is_mergeable = false;
 
     void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&);
     LayoutUnit ResolvePercentInlineSize(
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index d981861a..03c16f5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -24,6 +24,55 @@
 
 namespace {
 
+// Mergeable columns cannot be distributed to.
+// Make at least one spanned column is distributable.
+void EnsureDistributableColumnExists(
+    wtf_size_t start_column_index,
+    wtf_size_t span,
+    NGTableTypes::Columns* column_constraints) {
+  if (span == 0)
+    return;
+  DCHECK_LT(start_column_index, column_constraints->data.size());
+  wtf_size_t effective_span =
+      std::min(span, column_constraints->data.size() - start_column_index);
+  if (effective_span == 0)
+    return;
+  NGTableTypes::Column* start_column =
+      &column_constraints->data[start_column_index];
+  NGTableTypes::Column* end_column = start_column + effective_span;
+
+  NGTableTypes::Column* first_mergeable_column = nullptr;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (!column->is_collapsed) {
+      if (!column->is_mergeable) {
+        // Found non-collapsed, non mergeable column, nothing to do.
+        return;
+      } else if (!first_mergeable_column) {
+        // Found first non-collapsed, mergeable column.
+        first_mergeable_column = column;
+      }
+    }
+  }
+  // The interesting problem being solved here is interaction between
+  // collapsed and mergeable columns.
+  // All columns that are created by colspanned cell are mergeable by
+  // default. Without collapsing, the first column would always be
+  // marked as !mergeable.
+  // What to do if the first column collapses? If that was the only
+  // non-mergeable column, the entire cell would merge into first column,
+  // and collapse.
+  // To prevent "whole cell hidden if 1st cell is collapsed",
+  // we try to make first non-collapsed column mergeable.
+  // If all columns collapse, first cell is marked as meargable.
+  if (first_mergeable_column) {
+    // Some columns were not collapsed, mark first as mergeable.
+    first_mergeable_column->is_mergeable = false;
+  } else {
+    start_column->is_mergeable = false;
+  }
+}
+
 // Applies cell/wide cell constraints to columns.
 // Guarantees columns min/max widths have non-empty values.
 void ApplyCellConstraintsToColumnConstraints(
@@ -32,8 +81,37 @@
     bool is_fixed_layout,
     NGTableTypes::ColspanCells* colspan_cell_constraints,
     NGTableTypes::Columns* column_constraints) {
-  if (column_constraints->data.size() < cell_constraints.size())
-    column_constraints->data.resize(cell_constraints.size());
+  // Satisfy prerequisites for cell merging:
+
+  if (column_constraints->data.size() < cell_constraints.size()) {
+    // Column constraint must exist for each cell.
+    NGTableTypes::Column default_column;
+    default_column.is_table_fixed = is_fixed_layout;
+    default_column.is_mergeable = !is_fixed_layout;
+    wtf_size_t column_count =
+        cell_constraints.size() - column_constraints->data.size();
+    // Must loop because WTF::Vector does not support resize with default value.
+    for (wtf_size_t i = 0; i < column_count; ++i)
+      column_constraints->data.push_back(default_column);
+    DCHECK_EQ(column_constraints->data.size(), cell_constraints.size());
+
+  } else if (column_constraints->data.size() > cell_constraints.size()) {
+    // Trim mergeable columns off the end.
+    wtf_size_t last_non_merged_column = column_constraints->data.size() - 1;
+    while (last_non_merged_column + 1 > cell_constraints.size() &&
+           column_constraints->data[last_non_merged_column].is_mergeable) {
+      --last_non_merged_column;
+    }
+    column_constraints->data.resize(last_non_merged_column + 1);
+    DCHECK_GE(column_constraints->data.size(), cell_constraints.size());
+  }
+  // Make sure there exists a non-mergeable column for each colspanned cell.
+  for (const NGTableTypes::ColspanCell& colspan_cell :
+       *colspan_cell_constraints) {
+    EnsureDistributableColumnExists(colspan_cell.start_column,
+                                    colspan_cell.span, column_constraints);
+  }
+
   // Distribute cell constraints to column constraints.
   for (wtf_size_t i = 0; i < cell_constraints.size(); ++i) {
     column_constraints->data[i].Encompass(cell_constraints[i]);
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc
index b1a37a9a..cbd8f30 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc
@@ -72,9 +72,8 @@
     CheckForColorChange(element, svg_names::kLightingColorAttr, diff,
                         old_style->LightingColor(), style.LightingColor());
   }
-  const SVGComputedStyle& new_style = style.SvgStyle();
-  if (new_style.ColorInterpolationFilters() !=
-      old_style->SvgStyle().ColorInterpolationFilters()) {
+  if (style.ColorInterpolationFilters() !=
+      old_style->ColorInterpolationFilters()) {
     element.PrimitiveAttributeChanged(
         svg_names::kColorInterpolationFiltersAttr);
   }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
index 37a57a9..90d34cd 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
@@ -56,7 +56,7 @@
   PaintRecordBuilder builder(context);
 
   ColorFilter mask_content_filter =
-      StyleRef().SvgStyle().ColorInterpolation() == CI_LINEARRGB
+      StyleRef().ColorInterpolation() == CI_LINEARRGB
           ? kColorFilterSRGBToLinearRGB
           : kColorFilterNone;
   builder.Context().SetColorFilter(mask_content_filter);
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
index 222aba7c..671dc58 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
@@ -423,14 +423,15 @@
     return true;
 
   // TODO(chrishtr): support rect-based intersections in the cases below.
-  const SVGComputedStyle& svg_style = StyleRef().SvgStyle();
+  const ComputedStyle& style = StyleRef();
+  const SVGComputedStyle& svg_style = style.SvgStyle();
   if (hit_rules.can_hit_stroke &&
       (svg_style.HasStroke() || !hit_rules.require_stroke) &&
       StrokeContains(local_location, hit_rules.require_stroke))
     return true;
-  WindRule fill_rule = svg_style.FillRule();
+  WindRule fill_rule = style.FillRule();
   if (request.SvgClipContent())
-    fill_rule = svg_style.ClipRule();
+    fill_rule = style.ClipRule();
   if (hit_rules.can_hit_fill &&
       (svg_style.HasFill() || !hit_rules.require_fill) &&
       FillContains(local_location, hit_rules.require_fill, fill_rule))
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
index 26e0bf8..d2e64cc 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
@@ -307,10 +307,10 @@
     if (WriteSVGPaint(ts, object, svg_style.FillPaint(), GetCSSPropertyFill(),
                       "fill")) {
       WriteIfNotDefault(ts, "opacity", svg_style.FillOpacity(), 1.0f);
-      WriteIfNotDefault(ts, "fill rule", svg_style.FillRule(), RULE_NONZERO);
+      WriteIfNotDefault(ts, "fill rule", style.FillRule(), RULE_NONZERO);
       ts << "}]";
     }
-    WriteIfNotDefault(ts, "clip rule", svg_style.ClipRule(), RULE_NONZERO);
+    WriteIfNotDefault(ts, "clip rule", style.ClipRule(), RULE_NONZERO);
   }
 
   TreeScope& tree_scope = object.GetDocument();
@@ -433,7 +433,7 @@
   LineLayoutSVGInlineText text_line_layout =
       LineLayoutSVGInlineText(text_box->GetLineLayoutItem());
 
-  const SVGComputedStyle& svg_style = text_line_layout.StyleRef().SvgStyle();
+  const ComputedStyle& style = text_line_layout.StyleRef();
   String text = text_box->GetLineLayoutItem().GetText();
 
   unsigned fragments_size = fragments.size();
@@ -447,9 +447,8 @@
     // FIXME: Remove this hack, once the new text layout engine is completly
     // landed. We want to preserve the old web test results for now.
     ts << "chunk 1 ";
-    ETextAnchor anchor = svg_style.TextAnchor();
-    bool is_vertical_text =
-        !text_line_layout.StyleRef().IsHorizontalWritingMode();
+    ETextAnchor anchor = style.TextAnchor();
+    bool is_vertical_text = !style.IsHorizontalWritingMode();
     if (anchor == TA_MIDDLE) {
       ts << "(middle anchor";
       if (is_vertical_text)
diff --git a/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc b/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc
index f097849..b9d9ef7 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc
@@ -29,7 +29,7 @@
 
 float CalculateTextAnchorShift(const ComputedStyle& style, float length) {
   bool is_ltr = style.IsLeftToRightDirection();
-  switch (style.SvgStyle().TextAnchor()) {
+  switch (style.TextAnchor()) {
     default:
       NOTREACHED();
       FALLTHROUGH;
@@ -46,7 +46,7 @@
 
 bool NeedsTextAnchorAdjustment(const ComputedStyle& style) {
   bool is_ltr = style.IsLeftToRightDirection();
-  switch (style.SvgStyle().TextAnchor()) {
+  switch (style.TextAnchor()) {
     default:
       NOTREACHED();
       FALLTHROUGH;
diff --git a/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine_baseline.cc b/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine_baseline.cc
index edf089d..2ee571b 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine_baseline.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine_baseline.cc
@@ -62,9 +62,7 @@
   DCHECK(text_line_layout);
   DCHECK(text_line_layout.Style());
 
-  const SVGComputedStyle& style = text_line_layout.StyleRef().SvgStyle();
-
-  EDominantBaseline baseline = style.DominantBaseline();
+  EDominantBaseline baseline = text_line_layout.StyleRef().DominantBaseline();
   if (baseline == DB_AUTO) {
     if (is_vertical_text)
       baseline = DB_CENTRAL;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index e95c2dd0..a0102a98 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -631,6 +631,16 @@
   return kSPANavTypeSameDocumentBackwardOrForward;
 }
 
+void DocumentLoader::RunURLAndHistoryUpdateSteps(
+    const KURL& new_url,
+    scoped_refptr<SerializedScriptValue> data,
+    mojom::blink::ScrollRestorationType scroll_restoration_type,
+    WebFrameLoadType type) {
+  UpdateForSameDocumentNavigation(new_url, kSameDocumentNavigationHistoryApi,
+                                  std::move(data), scroll_restoration_type,
+                                  type, true);
+}
+
 void DocumentLoader::UpdateForSameDocumentNavigation(
     const KURL& new_url,
     SameDocumentNavigationSource same_document_navigation_source,
@@ -950,7 +960,14 @@
   }
 }
 
-void DocumentLoader::HandleRedirect(const KURL& current_request_url) {
+void DocumentLoader::HandleRedirect(
+    WebNavigationParams::RedirectInfo& redirect) {
+  ResourceResponse redirect_response =
+      redirect.redirect_response.ToResourceResponse();
+  const KURL& url_before_redirect = redirect_response.CurrentRequestUrl();
+  url_ = redirect.new_url;
+  const KURL& url_after_redirect = url_;
+
   // Browser process should have already checked that redirecting url is
   // allowed to display content from the target origin.
   // When the referrer page is in an unsigned Web Bundle file in local
@@ -959,12 +976,35 @@
   // to the file's URL (file:///tmp/a.wbn?https://example.com/page.html). In
   // this case, CanDisplay() returns false, and web_bundle_claimed_url must not
   // be null.
-  CHECK(SecurityOrigin::Create(current_request_url)->CanDisplay(url_) ||
+  CHECK(SecurityOrigin::Create(url_before_redirect)
+            ->CanDisplay(url_after_redirect) ||
         !params_->web_bundle_claimed_url.IsNull());
 
+  // Update the HTTP method of this document to the method used by the redirect.
+  AtomicString new_http_method = redirect.new_http_method;
+  if (http_method_ != new_http_method) {
+    http_body_ = nullptr;
+    http_content_type_ = g_null_atom;
+    http_method_ = new_http_method;
+  }
+
+  if (redirect.new_referrer.IsEmpty()) {
+    referrer_ = Referrer(Referrer::NoReferrer(), redirect.new_referrer_policy);
+  } else {
+    referrer_ = Referrer(redirect.new_referrer, redirect.new_referrer_policy);
+  }
+
+  // TODO(dgozman): check whether clearing origin policy is intended behavior.
+  origin_policy_ = base::nullopt;
+  probe::WillSendNavigationRequest(
+      probe::ToCoreProbeSink(GetFrame()), main_resource_identifier_, this,
+      url_after_redirect, http_method_, http_body_.get());
+
+  navigation_timing_info_->AddRedirect(redirect_response, url_after_redirect);
+
   DCHECK(!GetTiming().FetchStart().is_null());
-  redirect_chain_.push_back(url_);
-  GetTiming().AddRedirect(current_request_url, url_);
+  redirect_chain_.push_back(url_after_redirect);
+  GetTiming().AddRedirect(url_before_redirect, url_after_redirect);
 }
 
 bool DocumentLoader::ShouldReportTimingInfoToParent() {
@@ -1387,31 +1427,8 @@
                                    main_resource_identifier_, this, url_,
                                    http_method_, http_body_.get());
 
-  for (size_t i = 0; i < params_->redirects.size(); ++i) {
-    WebNavigationParams::RedirectInfo& redirect = params_->redirects[i];
-    url_ = redirect.new_url;
-    AtomicString new_http_method = redirect.new_http_method;
-    if (http_method_ != new_http_method) {
-      http_body_ = nullptr;
-      http_content_type_ = g_null_atom;
-      http_method_ = new_http_method;
-    }
-    if (redirect.new_referrer.IsEmpty()) {
-      referrer_ =
-          Referrer(Referrer::NoReferrer(), redirect.new_referrer_policy);
-    } else {
-      referrer_ = Referrer(redirect.new_referrer, redirect.new_referrer_policy);
-    }
-
-    // TODO(dgozman): check whether clearing origin policy is intended behavior.
-    origin_policy_ = base::nullopt;
-    probe::WillSendNavigationRequest(probe::ToCoreProbeSink(GetFrame()),
-                                     main_resource_identifier_, this, url_,
-                                     http_method_, http_body_.get());
-    ResourceResponse redirect_response =
-        redirect.redirect_response.ToResourceResponse();
-    navigation_timing_info_->AddRedirect(redirect_response, url_);
-    HandleRedirect(redirect_response.CurrentRequestUrl());
+  for (WebNavigationParams::RedirectInfo& redirect : params_->redirects) {
+    HandleRedirect(redirect);
   }
 
   if (!frame_->IsMainFrame()) {
@@ -1904,6 +1921,10 @@
     // SameSiteSiblingToAboutBlank_CrossSiteTop testcase).  To limit (but not
     // eliminate :-/) incorrect cases we require that `owner_document`'s origin
     // is same origin with `requestor_origin`.
+    //
+    // TODO(https://crbug.com/1176291): Improve heuristics for finding the
+    // correct initiator, to properly inherit/alias `document.domain` in more
+    // cases.
     auto* owner_local_frame = DynamicTo<LocalFrame>(owner_frame);
     if (owner_local_frame &&
         (url_.IsAboutSrcdocURL() || !requestor_origin_ ||
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index abbb471..1d1353a 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -142,7 +142,11 @@
     return subresource_filter_.Get();
   }
 
+  // TODO(dcheng, japhet): Some day, Document::Url() will always match
+  // DocumentLoader::Url(), and one of them will be removed. Today is not that
+  // day though.
   const KURL& Url() const;
+
   const KURL& UrlForHistory() const;
   const AtomicString& HttpMethod() const;
   const Referrer& GetReferrer() const;
@@ -153,12 +157,21 @@
   void DidChangePerformanceTiming();
   void DidObserveInputDelay(base::TimeDelta input_delay);
   void DidObserveLoadingBehavior(LoadingBehaviorFlag);
+
+  // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
+  void RunURLAndHistoryUpdateSteps(
+      const KURL&,
+      scoped_refptr<SerializedScriptValue>,
+      mojom::blink::ScrollRestorationType =
+          mojom::blink::ScrollRestorationType::kAuto,
+      WebFrameLoadType = WebFrameLoadType::kReplaceCurrentItem);
   void UpdateForSameDocumentNavigation(const KURL&,
                                        SameDocumentNavigationSource,
                                        scoped_refptr<SerializedScriptValue>,
                                        mojom::blink::ScrollRestorationType,
                                        WebFrameLoadType,
                                        bool is_content_initiated);
+
   const ResourceResponse& GetResponse() const { return response_; }
   bool IsClientRedirect() const { return is_client_redirect_; }
   bool ReplacesCurrentHistoryItem() const {
@@ -287,11 +300,6 @@
 
   bool IsSameOriginNavigation() const { return is_same_origin_navigation_; }
 
-  // TODO(dcheng, japhet): Some day, Document::Url() will always match
-  // DocumentLoader::Url(), and one of them will be removed. Today is not that
-  // day though.
-  void UpdateUrlForDocumentOpen(const KURL& url) { url_ = url; }
-
   enum class HistoryNavigationType {
     kDifferentDocument,
     kFragment,
@@ -394,7 +402,9 @@
   void FinishedLoading(base::TimeTicks finish_time);
   void CancelLoadAfterCSPDenied(const ResourceResponse&);
 
-  void HandleRedirect(const KURL& current_request_url);
+  // Process a redirect to update the redirect chain, current URL, referrer,
+  // etc.
+  void HandleRedirect(WebNavigationParams::RedirectInfo& redirect);
   void HandleResponse();
 
   void InitializeEmptyResponse();
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index 9002164..c77d38a 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -337,7 +337,7 @@
 void PaintTiming::SetFirstContentfulPaintPresentation(base::TimeTicks stamp) {
   DCHECK(first_contentful_paint_presentation_.is_null());
   TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("benchmark,loading",
-                                      "FirstContentfulPaint",
+                                      "GlobalFirstContentfulPaint",
                                       TRACE_EVENT_SCOPE_GLOBAL, stamp);
   first_contentful_paint_presentation_ = stamp;
   probe::PaintTiming(
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
index cc25334e..888f677c 100644
--- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
@@ -172,7 +172,7 @@
     }
 
     for (int i = 0; i < 3; i++) {
-      switch (svg_style.PaintOrderType(i)) {
+      switch (style.PaintOrderType(i)) {
         case PT_FILL:
           if (has_fill) {
             PaintText(paint_info, style, *selection_style, fragment,
@@ -345,7 +345,7 @@
   const SVGComputedStyle& svg_decoration_style = decoration_style.SvgStyle();
 
   for (int i = 0; i < 3; i++) {
-    switch (svg_decoration_style.PaintOrderType(i)) {
+    switch (decoration_style.PaintOrderType(i)) {
       case PT_FILL:
         if (svg_decoration_style.HasFill()) {
           PaintFlags fill_flags;
diff --git a/third_party/blink/renderer/core/paint/svg_shape_painter.cc b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
index fee3b8b2..dc3a1756 100644
--- a/third_party/blink/renderer/core/paint/svg_shape_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
@@ -35,10 +35,10 @@
 }
 
 static SkPathFillType FillRuleFromStyle(const PaintInfo& paint_info,
-                                        const SVGComputedStyle& svg_style) {
+                                        const ComputedStyle& style) {
   return WebCoreWindRuleToSkFillType(paint_info.IsRenderingClipPathAsMaskImage()
-                                         ? svg_style.ClipRule()
-                                         : svg_style.FillRule());
+                                         ? style.ClipRule()
+                                         : style.FillRule());
 }
 
 void SVGShapePainter::Paint(const PaintInfo& paint_info) {
@@ -63,23 +63,23 @@
       SVGModelObjectPainter::RecordHitTestData(layout_svg_shape_, paint_info);
       SVGDrawingRecorder recorder(paint_info.context, layout_svg_shape_,
                                   paint_info.phase);
-      const SVGComputedStyle& svg_style =
-          layout_svg_shape_.StyleRef().SvgStyle();
+      const ComputedStyle& style = layout_svg_shape_.StyleRef();
+      const SVGComputedStyle& svg_style = style.SvgStyle();
 
-      bool should_anti_alias = svg_style.ShapeRendering() != SR_CRISPEDGES &&
-                               svg_style.ShapeRendering() != SR_OPTIMIZESPEED;
+      bool should_anti_alias = style.ShapeRendering() != SR_CRISPEDGES &&
+                               style.ShapeRendering() != SR_OPTIMIZESPEED;
 
       for (int i = 0; i < 3; i++) {
-        switch (svg_style.PaintOrderType(i)) {
+        switch (style.PaintOrderType(i)) {
           case PT_FILL: {
             PaintFlags fill_flags;
             if (!SVGObjectPainter(layout_svg_shape_)
-                     .PreparePaint(paint_info, layout_svg_shape_.StyleRef(),
-                                   kApplyToFillMode, fill_flags))
+                     .PreparePaint(paint_info, style, kApplyToFillMode,
+                                   fill_flags))
               break;
             fill_flags.setAntiAlias(should_anti_alias);
             FillShape(paint_info.context, fill_flags,
-                      FillRuleFromStyle(paint_info, svg_style));
+                      FillRuleFromStyle(paint_info, style));
             break;
           }
           case PT_STROKE:
@@ -99,15 +99,14 @@
               PaintFlags stroke_flags;
               if (!SVGObjectPainter(layout_svg_shape_)
                        .PreparePaint(
-                           paint_info, layout_svg_shape_.StyleRef(),
-                           kApplyToStrokeMode, stroke_flags,
+                           paint_info, style, kApplyToStrokeMode, stroke_flags,
                            base::OptionalOrNullptr(non_scaling_transform)))
                 break;
               stroke_flags.setAntiAlias(should_anti_alias);
 
               StrokeData stroke_data;
               SVGLayoutSupport::ApplyStrokeStyleToStrokeData(
-                  stroke_data, layout_svg_shape_.StyleRef(), layout_svg_shape_,
+                  stroke_data, style, layout_svg_shape_,
                   layout_svg_shape_.DashScaleFactor());
               stroke_data.SetupPaint(&stroke_flags);
 
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 34d2fd1..812182b 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -2434,6 +2434,45 @@
   return GetListStyleType()->GetStringValue();
 }
 
+static const int kPaintOrderBitwidth = 2;
+
+static unsigned PaintOrderSequence(EPaintOrderType first,
+                                   EPaintOrderType second,
+                                   EPaintOrderType third) {
+  return (((third << kPaintOrderBitwidth) | second) << kPaintOrderBitwidth) |
+         first;
+}
+
+EPaintOrderType ComputedStyle::PaintOrderType(unsigned index) const {
+  unsigned pt = 0;
+  DCHECK(index < ((1 << kPaintOrderBitwidth) - 1));
+  switch (PaintOrder()) {
+    case kPaintOrderNormal:
+    case kPaintOrderFillStrokeMarkers:
+      pt = PaintOrderSequence(PT_FILL, PT_STROKE, PT_MARKERS);
+      break;
+    case kPaintOrderFillMarkersStroke:
+      pt = PaintOrderSequence(PT_FILL, PT_MARKERS, PT_STROKE);
+      break;
+    case kPaintOrderStrokeFillMarkers:
+      pt = PaintOrderSequence(PT_STROKE, PT_FILL, PT_MARKERS);
+      break;
+    case kPaintOrderStrokeMarkersFill:
+      pt = PaintOrderSequence(PT_STROKE, PT_MARKERS, PT_FILL);
+      break;
+    case kPaintOrderMarkersFillStroke:
+      pt = PaintOrderSequence(PT_MARKERS, PT_FILL, PT_STROKE);
+      break;
+    case kPaintOrderMarkersStrokeFill:
+      pt = PaintOrderSequence(PT_MARKERS, PT_STROKE, PT_FILL);
+      break;
+  }
+
+  pt =
+      (pt >> (kPaintOrderBitwidth * index)) & ((1u << kPaintOrderBitwidth) - 1);
+  return static_cast<EPaintOrderType>(pt);
+}
+
 STATIC_ASSERT_ENUM(cc::OverscrollBehavior::Type::kAuto,
                    EOverscrollBehavior::kAuto);
 STATIC_ASSERT_ENUM(cc::OverscrollBehavior::Type::kContain,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 73a6470..d4a11ff 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1151,6 +1151,19 @@
   const Length& Ry() const { return SvgStyle().Ry(); }
   void SetRy(const Length& ry) { AccessSVGStyle().SetRy(ry); }
 
+  WindRule ClipRule() const { return SvgStyle().ClipRule(); }
+  EColorInterpolation ColorInterpolation() const {
+    return SvgStyle().ColorInterpolation();
+  }
+  EColorInterpolation ColorInterpolationFilters() const {
+    return SvgStyle().ColorInterpolationFilters();
+  }
+  EColorRendering ColorRendering() const { return SvgStyle().ColorRendering(); }
+  EDominantBaseline DominantBaseline() const {
+    return SvgStyle().DominantBaseline();
+  }
+  WindRule FillRule() const { return SvgStyle().FillRule(); }
+
   // fill-opacity
   float FillOpacity() const { return SvgStyle().FillOpacity(); }
   void SetFillOpacity(float f) { AccessSVGStyle().SetFillOpacity(f); }
@@ -1181,6 +1194,12 @@
   float StopOpacity() const { return SvgStyle().StopOpacity(); }
   void SetStopOpacity(float f) { AccessSVGStyle().SetStopOpacity(f); }
 
+  // paint-order helper
+  EPaintOrder PaintOrder() const { return SvgStyle().PaintOrder(); }
+  EPaintOrderType PaintOrderType(unsigned index) const;
+
+  EShapeRendering ShapeRendering() const { return SvgStyle().ShapeRendering(); }
+
   // stroke-dasharray
   SVGDashArray* StrokeDashArray() const { return SvgStyle().StrokeDashArray(); }
   void SetStrokeDashArray(scoped_refptr<SVGDashArray> array) {
@@ -1209,6 +1228,7 @@
     AccessSVGStyle().SetStrokeWidth(w);
   }
 
+  ETextAnchor TextAnchor() const { return SvgStyle().TextAnchor(); }
   EVectorEffect VectorEffect() const { return SvgStyle().VectorEffect(); }
 
   // Comparison operators
diff --git a/third_party/blink/renderer/core/style/svg_computed_style.cc b/third_party/blink/renderer/core/style/svg_computed_style.cc
index 6d4354b..ab67196 100644
--- a/third_party/blink/renderer/core/style/svg_computed_style.cc
+++ b/third_party/blink/renderer/core/style/svg_computed_style.cc
@@ -33,8 +33,6 @@
 
 namespace blink {
 
-static const int kPaintOrderBitwidth = 2;
-
 SVGComputedStyle::SVGComputedStyle() {
   static SVGComputedStyle* initial_style = new SVGComputedStyle(kCreateInitial);
 
@@ -249,43 +247,6 @@
   return false;
 }
 
-unsigned PaintOrderSequence(EPaintOrderType first,
-                            EPaintOrderType second,
-                            EPaintOrderType third) {
-  return (((third << kPaintOrderBitwidth) | second) << kPaintOrderBitwidth) |
-         first;
-}
-
-EPaintOrderType SVGComputedStyle::PaintOrderType(unsigned index) const {
-  unsigned pt = 0;
-  DCHECK(index < ((1 << kPaintOrderBitwidth) - 1));
-  switch (this->PaintOrder()) {
-    case kPaintOrderNormal:
-    case kPaintOrderFillStrokeMarkers:
-      pt = PaintOrderSequence(PT_FILL, PT_STROKE, PT_MARKERS);
-      break;
-    case kPaintOrderFillMarkersStroke:
-      pt = PaintOrderSequence(PT_FILL, PT_MARKERS, PT_STROKE);
-      break;
-    case kPaintOrderStrokeFillMarkers:
-      pt = PaintOrderSequence(PT_STROKE, PT_FILL, PT_MARKERS);
-      break;
-    case kPaintOrderStrokeMarkersFill:
-      pt = PaintOrderSequence(PT_STROKE, PT_MARKERS, PT_FILL);
-      break;
-    case kPaintOrderMarkersFillStroke:
-      pt = PaintOrderSequence(PT_MARKERS, PT_FILL, PT_STROKE);
-      break;
-    case kPaintOrderMarkersStrokeFill:
-      pt = PaintOrderSequence(PT_MARKERS, PT_STROKE, PT_FILL);
-      break;
-  }
-
-  pt =
-      (pt >> (kPaintOrderBitwidth * index)) & ((1u << kPaintOrderBitwidth) - 1);
-  return (EPaintOrderType)pt;
-}
-
 void SVGComputedStyle::SetMaskerResource(
     scoped_refptr<StyleSVGResource> resource) {
   if (!(resources->masker == resource))
diff --git a/third_party/blink/renderer/core/style/svg_computed_style.h b/third_party/blink/renderer/core/style/svg_computed_style.h
index a70adb7e7..319bda6 100644
--- a/third_party/blink/renderer/core/style/svg_computed_style.h
+++ b/third_party/blink/renderer/core/style/svg_computed_style.h
@@ -347,7 +347,6 @@
   EPaintOrder PaintOrder() const {
     return (EPaintOrder)svg_inherited_flags.paint_order;
   }
-  EPaintOrderType PaintOrderType(unsigned index) const;
 
   const SVGPaint& InternalVisitedFillPaint() const {
     return fill->visited_link_paint;
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index fd4521d..8a6f613 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -668,7 +668,7 @@
     return SMILRepeatCount::Indefinite();
   bool ok;
   double result = value.ToDouble(&ok);
-  if (ok && result > 0)
+  if (ok && result > 0 && std::isfinite(result))
     return SMILRepeatCount::Numeric(result);
   return SMILRepeatCount::Unspecified();
 }
diff --git a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
index 3dd66330..9c6d6c6 100644
--- a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
+++ b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
@@ -137,7 +137,7 @@
     SVGElement& element,
     EColorInterpolation parent_color_interpolation) {
   if (const LayoutObject* layout_object = element.GetLayoutObject())
-    return layout_object->StyleRef().SvgStyle().ColorInterpolationFilters();
+    return layout_object->StyleRef().ColorInterpolationFilters();
 
   // No layout has been performed, try to determine the property value
   // "manually" (used by external SVG files.)
diff --git a/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.cc b/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.cc
index ea88fd7..6bf7616c 100644
--- a/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.cc
+++ b/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.cc
@@ -86,7 +86,7 @@
   DCHECK(attr_name == svg_names::kColorInterpolationFiltersAttr);
   DCHECK(GetLayoutObject());
   EColorInterpolation color_interpolation =
-      GetLayoutObject()->StyleRef().SvgStyle().ColorInterpolationFilters();
+      GetLayoutObject()->StyleRef().ColorInterpolationFilters();
   InterpolationSpace resolved_interpolation_space =
       SVGFilterBuilder::ResolveInterpolationSpace(color_interpolation);
   if (resolved_interpolation_space == effect->OperatingInterpolationSpace())
diff --git a/third_party/blink/renderer/core/svg/svg_geometry_element.cc b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
index 448c482..4a54d968 100644
--- a/third_party/blink/renderer/core/svg/svg_geometry_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
@@ -94,7 +94,7 @@
     return false;
 
   // Path::Contains will reject points with a non-finite component.
-  WindRule fill_rule = layout_object->StyleRef().SvgStyle().FillRule();
+  WindRule fill_rule = layout_object->StyleRef().FillRule();
   return AsPath().Contains(point->Target()->Value(), fill_rule);
 }
 
@@ -140,7 +140,7 @@
 
   DCHECK(GetLayoutObject());
   DCHECK(GetLayoutObject()->Style());
-  path.SetWindRule(GetLayoutObject()->StyleRef().SvgStyle().ClipRule());
+  path.SetWindRule(GetLayoutObject()->StyleRef().ClipRule());
   return path;
 }
 
diff --git a/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc b/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
index 3c40a9b..ade1a80 100644
--- a/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
+++ b/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
@@ -85,12 +85,6 @@
       local_frame->GetSettings()->GetWebSecurityEnabled()) {
     return ApiStatus::kNotAvailableDueToCrossOriginIsolation;
   }
-  // CrossOriginIsolated is also set for same-agent cross-origin iframe.
-  // Allow only iframes that have the same origin as the main frame.
-  // Note that COOP guarantees that all main frames have the same origin.
-  if (local_frame->IsCrossOriginToMainFrame()) {
-    return ApiStatus::kNotAvailableDueToCrossOriginContext;
-  }
 
   // We need DocumentResourceCoordinator to query PerformanceManager.
   if (!window->document()) {
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 d5a3bcbf..b3e8286 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -133,7 +133,6 @@
   ax::mojom::blink::Role role = obj->RoleValue();
   return role == ax::mojom::blink::Role::kGroup ||
          role == ax::mojom::blink::Role::kGenericContainer ||
-         role == ax::mojom::blink::Role::kIgnored ||
          role == ax::mojom::blink::Role::kRowGroup;
 }
 
@@ -293,12 +292,6 @@
     return kIgnoreObject;
   }
 
-  if (RoleValue() == ax::mojom::blink::Role::kIgnored) {
-    if (ignored_reasons)
-      ignored_reasons->push_back(IgnoredReason(kAXUninteresting));
-    return kIgnoreObject;
-  }
-
   if (HasInheritedPresentationalRole()) {
     if (ignored_reasons) {
       const AXObject* inherits_from = InheritsPresentationalRoleFrom();
@@ -715,7 +708,7 @@
     return ax::mojom::blink::Role::kGenericContainer;
 
   if (parent->RoleValue() == ax::mojom::blink::Role::kLayoutTable)
-    return ax::mojom::blink::Role::kIgnored;
+    return ax::mojom::blink::Role::kGenericContainer;
 
   return ax::mojom::blink::Role::kRowGroup;
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 808fa8eb..ffcad28 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -105,6 +105,23 @@
   return node->parentNode();
 }
 
+#if DCHECK_IS_ON()
+bool IsValidRole(ax::mojom::blink::Role role, bool is_virtual_object) {
+  // Check for illegal roles that should not be assigned in Blink.
+  switch (role) {
+    case ax::mojom::blink::Role::kUnknown:
+      // Role::kUnknown is allowed on virtual objects with no set role.
+      return is_virtual_object;
+    case ax::mojom::blink::Role::kColumn:
+    case ax::mojom::blink::Role::kTableHeaderContainer:
+    case ax::mojom::blink::Role::kIgnored:
+      return false;
+    default:
+      return true;
+  }
+}
+#endif
+
 struct RoleHashTraits : HashTraits<ax::mojom::blink::Role> {
   static const bool kEmptyValueIsZero = true;
   static ax::mojom::blink::Role EmptyValue() {
@@ -645,8 +662,11 @@
   // Note: in order to avoid reentrancy, the role computation cannot use the
   // ParentObject(), although it can use the DOM parent.
   role_ = DetermineAccessibilityRole();
-  DCHECK(role_ != ax::mojom::blink::Role::kUnknown || IsVirtualObject())
-      << "Illegal Role::kUnknown for " << GetNode() << " " << GetLayoutObject();
+#if DCHECK_IS_ON()
+  DCHECK(IsValidRole(role_, IsVirtualObject()))
+      << "Illegal " << role_ << " for " << GetNode() << " "
+      << GetLayoutObject();
+#endif
 
   // Determine the parent as soon as possible.
   // Every AXObject must have a parent unless it's the root.
@@ -2235,13 +2255,10 @@
   if (IsA<HTMLHtmlElement>(GetNode()))
     return RuntimeEnabledFeatures::AccessibilityExposeHTMLElementEnabled();
 
-  // If the node is part of the user agent shadow dom, or has the explicit
-  // internal Role::kIgnored, they aren't interesting for paragraph navigation
-  // or LabelledBy/DescribedBy relationships.
-  if (RoleValue() == ax::mojom::blink::Role::kIgnored ||
-      GetNode()->IsInUserAgentShadowRoot()) {
+  // If the node is part of the user agent shadow dom, it isn't interesting for
+  // paragraph navigation or LabelledBy/DescribedBy relationships.
+  if (GetNode()->IsInUserAgentShadowRoot())
     return false;
-  }
 
   // Keep the internal accessibility tree consistent for videos which lack
   // a player and also inner text.
@@ -4983,7 +5000,6 @@
     case ax::mojom::blink::Role::kFooterAsNonLandmark:
     case ax::mojom::blink::Role::kGenericContainer:
     case ax::mojom::blink::Role::kHeaderAsNonLandmark:
-    case ax::mojom::blink::Role::kIgnored:
     case ax::mojom::blink::Role::kImageMap:
     case ax::mojom::blink::Role::kInlineTextBox:
     case ax::mojom::blink::Role::kLabelText:
@@ -5071,6 +5087,7 @@
     }
 
     case ax::mojom::blink::Role::kColumn:
+    case ax::mojom::blink::Role::kIgnored:
     case ax::mojom::blink::Role::kTableHeaderContainer:
     case ax::mojom::blink::Role::kUnknown:
       NOTREACHED() << "Role shouldn't occur in Blink: " << ToString(true, true);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 41e72af..b5dcead9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -674,7 +674,7 @@
   if (GetState().GetTransform() == new_transform)
     return;
 
-  // Must call setTransform to set the IsTransformInvertible flag.
+  // We need to call SetTransform() to set the IsTransformInvertible flag.
   ModifiableState().SetTransform(new_transform);
   if (!GetState().IsTransformInvertible())
     return;
@@ -683,6 +683,34 @@
   path_.Transform(translation_matrix.Inverse());
 }
 
+void BaseRenderingContext2D::perspective(double length) {
+  cc::PaintCanvas* c = GetOrCreatePaintCanvas();
+  if (!c)
+    return;
+
+  if (length == 0 || !std::isfinite(length))
+    return;
+
+  float flength = clampTo<float>(length);
+
+  TransformationMatrix perspective_matrix =
+      TransformationMatrix().ApplyPerspective(flength);
+
+  // Check if the transformation is a no-op and early out if that is the case.
+  TransformationMatrix new_transform =
+      GetState().GetTransform().ApplyPerspective(flength);
+  if (GetState().GetTransform() == new_transform)
+    return;
+
+  // We need to call SetTransform() to set the IsTransformInvertible flag.
+  ModifiableState().SetTransform(new_transform);
+  if (!GetState().IsTransformInvertible())
+    return;
+
+  c->concat(TransformationMatrix::ToSkM44(perspective_matrix));
+  path_.Transform(perspective_matrix.Inverse());
+}
+
 void BaseRenderingContext2D::transform(double m11,
                                        double m12,
                                        double m13,
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index df1c44e..56541ea 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -91,6 +91,7 @@
                   double angle_in_radians);
   void translate(double tx, double ty);
   void translate(double tx, double ty, double tz);
+  void perspective(double length);
   void transform(double m11,
                  double m12,
                  double m21,
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
index 6d74856a..0956b79 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
@@ -63,6 +63,7 @@
     [RuntimeEnabled=NewCanvas2DAPI] void rotateAxis(unrestricted double axisX, unrestricted double axisY, unrestricted double axisZ, unrestricted double angle);
     void translate(unrestricted double x, unrestricted double y);
     [RuntimeEnabled=NewCanvas2DAPI] void translate(unrestricted double x, unrestricted double y, unrestricted double z);
+    [RuntimeEnabled=NewCanvas2DAPI] void perspective(unrestricted double length);
     void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
     [RuntimeEnabled=NewCanvas2DAPI] void transform(
         unrestricted double m11, unrestricted double m12, unrestricted double m13, unrestricted double m14,
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
index 27317048..50727f90 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
@@ -27,6 +27,7 @@
     [RuntimeEnabled=NewCanvas2DAPI] void rotateAxis(unrestricted double axisX, unrestricted double axisY, unrestricted double axisZ, unrestricted double angle);
     void translate(unrestricted double x, unrestricted double y);
     [RuntimeEnabled=NewCanvas2DAPI] void translate(unrestricted double x, unrestricted double y, unrestricted double z);
+    [RuntimeEnabled=NewCanvas2DAPI] void perspective(unrestricted double length);
     void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
     [RuntimeEnabled=NewCanvas2DAPI] void transform(
         unrestricted double m11, unrestricted double m12, unrestricted double m13, unrestricted double m14,
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
index 29a721f..6e4ef60d 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
@@ -19,6 +19,7 @@
     [RuntimeEnabled=NewCanvas2DAPI] void rotateAxis(unrestricted double axisX, unrestricted double axisY, unrestricted double axisZ, unrestricted double angle);
     void translate(unrestricted double x, unrestricted double y);
     [RuntimeEnabled=NewCanvas2DAPI] void translate(unrestricted double x, unrestricted double y, unrestricted double z);
+    [RuntimeEnabled=NewCanvas2DAPI] void perspective(unrestricted double length);
     void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
     [RuntimeEnabled=NewCanvas2DAPI] void transform(
         unrestricted double m11, unrestricted double m12, unrestricted double m13, unrestricted double m14,
diff --git a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.cc b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.cc
index 9c069b9..4de9496a 100644
--- a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.h"
 
+#include "media/base/audio_timestamp_helper.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -78,7 +79,13 @@
     last_frames_ = audio_bus.frames();
   }
 
-  DeliverDataToTracks(audio_bus, base::TimeTicks() + data->timestamp());
+  // data->timestamp() is the time at the beginning of the |data| audio piece.
+  // |capture_time| is the time at the end of the |data| audio piece.
+  base::TimeTicks capture_time = base::TimeTicks() + data->timestamp() +
+                                 media::AudioTimestampHelper::FramesToTime(
+                                     audio_bus.frames(), sample_rate);
+
+  DeliverDataToTracks(audio_bus, capture_time);
 }
 
 bool PushableMediaStreamAudioSource::EnsureSourceIsStarted() {
diff --git a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source_test.cc b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source_test.cc
index 7c4f45e4..f95874f1 100644
--- a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source_test.cc
@@ -138,12 +138,15 @@
     }
 
     base::RunLoop run_loop;
-    base::TimeTicks reference_time = base::TimeTicks::Now();
-    fake_sink->SetDataTimeExpectation(reference_time, run_loop.QuitClosure());
+    base::TimeTicks timestamp = base::TimeTicks::Now();
+    base::TimeDelta duration =
+        base::TimeDelta::FromSeconds(1) * frames / sample_rate;
+    base::TimeTicks capture_time = timestamp + duration;
+    fake_sink->SetDataTimeExpectation(capture_time, run_loop.QuitClosure());
 
     pushable_audio_source_->PushAudioData(AudioFrameSerializationData::Wrap(
         media::AudioBus::Create(channels, frames), sample_rate,
-        reference_time - base::TimeTicks()));
+        timestamp - base::TimeTicks()));
     run_loop.Run();
 
     EXPECT_EQ(fake_sink->did_receive_format_change(), expect_format_change);
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
index e24b63e9..cdcdd0e 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -63,11 +63,11 @@
   Supplement<LocalDOMWindow>::Trace(visitor);
 }
 
-ScriptPromise DOMScheduler::postTask(ScriptState* script_state,
-                                     V8Function* callback_function,
-                                     SchedulerPostTaskOptions* options,
-                                     const HeapVector<ScriptValue>& args,
-                                     ExceptionState& exception_state) {
+ScriptPromise DOMScheduler::postTask(
+    ScriptState* script_state,
+    V8SchedulerPostTaskCallback* callback_function,
+    SchedulerPostTaskOptions* options,
+    ExceptionState& exception_state) {
   if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed())
     return RejectPromiseImmediately(exception_state);
   if (options->signal() && options->signal()->aborted())
@@ -112,8 +112,8 @@
       base::TimeDelta::FromMilliseconds(std::max(0, options->delay()));
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  MakeGarbageCollected<DOMTask>(this, resolver, callback_function, args,
-                                task_signal, delay);
+  MakeGarbageCollected<DOMTask>(this, resolver, callback_function, task_signal,
+                                delay);
   return resolver->Promise();
 }
 
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
index 1cec218..3014dff 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
@@ -21,8 +21,7 @@
 class DOMTaskSignal;
 class ExceptionState;
 class SchedulerPostTaskOptions;
-class ScriptValue;
-class V8Function;
+class V8SchedulerPostTaskCallback;
 class WebSchedulingTaskQueue;
 
 class MODULES_EXPORT DOMScheduler : public ScriptWrappable,
@@ -44,9 +43,8 @@
   // underlying context is destroyed, e.g. for detached windows, this will
   // return a rejected promise.
   ScriptPromise postTask(ScriptState*,
-                         V8Function*,
+                         V8SchedulerPostTaskCallback*,
                          SchedulerPostTaskOptions*,
-                         const HeapVector<ScriptValue>& args,
                          ExceptionState&);
 
   // Returns a TaskSignal representing the state when the current task was
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task.cc b/third_party/blink/renderer/modules/scheduler/dom_task.cc
index b3174c6..856c465 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_task.cc
@@ -9,7 +9,7 @@
 #include "base/check_op.h"
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_callback.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/modules/scheduler/dom_scheduler.h"
@@ -33,13 +33,11 @@
 
 DOMTask::DOMTask(DOMScheduler* scheduler,
                  ScriptPromiseResolver* resolver,
-                 V8Function* callback,
-                 const HeapVector<ScriptValue>& args,
+                 V8SchedulerPostTaskCallback* callback,
                  DOMTaskSignal* signal,
                  base::TimeDelta delay)
     : scheduler_(scheduler),
       callback_(callback),
-      arguments_(args),
       resolver_(resolver),
       signal_(signal),
       // TODO(kdillon): Expose queuing time from base::sequence_manager so we
@@ -65,7 +63,6 @@
 void DOMTask::Trace(Visitor* visitor) const {
   visitor->Trace(scheduler_);
   visitor->Trace(callback_);
-  visitor->Trace(arguments_);
   visitor->Trace(resolver_);
   visitor->Trace(signal_);
 }
@@ -97,7 +94,7 @@
   v8_context->SetContinuationPreservedEmbedderData(
       ToV8(signal_.Get(), v8_context->Global(), isolate));
   ScriptValue result;
-  if (callback_->Invoke(nullptr, arguments_).To(&result))
+  if (callback_->Invoke(nullptr).To(&result))
     resolver_->Resolve(result.V8Value());
   else if (try_catch.HasCaught())
     resolver_->Reject(try_catch.Exception());
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task.h b/third_party/blink/renderer/modules/scheduler/dom_task.h
index 7c490b2..76c408e 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_task.h
@@ -15,8 +15,7 @@
 class DOMScheduler;
 class DOMTaskSignal;
 class ScriptState;
-class ScriptValue;
-class V8Function;
+class V8SchedulerPostTaskCallback;
 
 // DOMTask represents a task scheduled via the web scheduling API. It will
 // keep itself alive until DOMTask::Invoke is called, which may be after the
@@ -25,8 +24,7 @@
  public:
   DOMTask(DOMScheduler*,
           ScriptPromiseResolver*,
-          V8Function*,
-          const HeapVector<ScriptValue>& args,
+          V8SchedulerPostTaskCallback*,
           DOMTaskSignal*,
           base::TimeDelta delay);
 
@@ -44,8 +42,7 @@
 
   Member<DOMScheduler> scheduler_;
   TaskHandle task_handle_;
-  Member<V8Function> callback_;
-  HeapVector<ScriptValue> arguments_;
+  Member<V8SchedulerPostTaskCallback> callback_;
   Member<ScriptPromiseResolver> resolver_;
   probe::AsyncTaskId async_task_id_;
   Member<DOMTaskSignal> signal_;
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
index 54791a0e..a06f1bc 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -9,6 +9,6 @@
     ImplementedAs=DOMScheduler,
     RuntimeEnabled=WebScheduler
 ] interface Scheduler {
-    [CallWith=ScriptState, MeasureAs=SchedulerPostTask, RaisesException] Promise<any> postTask(Function callback, optional SchedulerPostTaskOptions options = {}, any... arguments);
+    [CallWith=ScriptState, MeasureAs=SchedulerPostTask, RaisesException] Promise<any> postTask(SchedulerPostTaskCallback callback, optional SchedulerPostTaskOptions options = {});
     [CallWith=ScriptState, MeasureAs=SchedulerCurrentTaskSignal] readonly attribute TaskSignal currentTaskSignal;
 };
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl
new file mode 100644
index 0000000..7dc1b251
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl
@@ -0,0 +1,7 @@
+// Copyright 2021 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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw
+callback SchedulerPostTaskCallback = any ();
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index ea7be1b94..2ad0ae8 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -218,11 +218,9 @@
         /*reporting_observer_receiver=*/mojo::NullReceiver());
 
     // To make the other side callable.
-    mojo::AssociateWithDisconnectedPipe(host_receiver.PassHandle());
-    mojo::AssociateWithDisconnectedPipe(
-        registration_object_host_receiver.PassHandle());
-    mojo::AssociateWithDisconnectedPipe(
-        service_worker_object_host_receiver.PassHandle());
+    host_receiver.EnableUnassociatedUsage();
+    registration_object_host_receiver.EnableUnassociatedUsage();
+    service_worker_object_host_receiver.EnableUnassociatedUsage();
   }
 
   void FailedToFetchClassicScript() override {
diff --git a/third_party/blink/renderer/modules/speech/OWNERS b/third_party/blink/renderer/modules/speech/OWNERS
index e807354..5ecb2f4d 100644
--- a/third_party/blink/renderer/modules/speech/OWNERS
+++ b/third_party/blink/renderer/modules/speech/OWNERS
@@ -3,4 +3,3 @@
 
 # Speech recognition - send reviews here first, then to dmazzoni for approval.
 tommi@chromium.org
-gshires@chromium.org
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 19fd47e..60d4353 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -41,23 +41,6 @@
 
 namespace {
 
-bool IsValidSkColorSpace(sk_sp<SkColorSpace> sk_color_space) {
-  // Refer to CanvasColorSpaceToGfxColorSpace in CanvasColorParams.
-  sk_sp<SkColorSpace> valid_sk_color_spaces[] = {
-      gfx::ColorSpace::CreateSRGB().ToSkColorSpace(),
-      gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace(),
-      gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
-                      gfx::ColorSpace::TransferID::GAMMA24)
-          .ToSkColorSpace()};
-  for (auto& valid_sk_color_space : valid_sk_color_spaces) {
-    if (SkColorSpace::Equals(sk_color_space.get(),
-                             valid_sk_color_space.get())) {
-      return true;
-    }
-  }
-  return false;
-}
-
 struct YUVReadbackContext {
   gfx::Size coded_size;
   gfx::Rect visible_rect;
@@ -189,18 +172,224 @@
 const base::TimeDelta CachedVideoFramePool::kIdleTimeout =
     base::TimeDelta::FromSeconds(10);
 
+scoped_refptr<viz::RasterContextProvider> GetRasterContextProvider() {
+  auto wrapper = SharedGpuContext::ContextProviderWrapper();
+  if (!wrapper)
+    return nullptr;
+
+  if (auto* provider = wrapper->ContextProvider())
+    return base::WrapRefCounted(provider->RasterContextProvider());
+
+  return nullptr;
+}
+
+bool CanUseZeroCopyImages(const media::VideoFrame& frame) {
+  // SharedImage optimization: create AcceleratedStaticBitmapImage directly.
+  // Disabled on Android because the hardware decode implementation may neuter
+  // frames, which would violate ImageBitmap requirements.
+  // TODO(sandersd): Handle YUV pixel formats.
+  // TODO(sandersd): Handle high bit depth formats.
+#if defined(OS_ANDROID)
+  return false;
+#else
+  return frame.NumTextures() == 1 &&
+         frame.mailbox_holder(0).mailbox.IsSharedImage() &&
+         (frame.format() == media::PIXEL_FORMAT_ARGB ||
+          frame.format() == media::PIXEL_FORMAT_XRGB ||
+          frame.format() == media::PIXEL_FORMAT_ABGR ||
+          frame.format() == media::PIXEL_FORMAT_XBGR ||
+          frame.format() == media::PIXEL_FORMAT_BGRA);
+#endif
+}
+
+bool PreferAcceleratedImages(const media::VideoFrame& frame) {
+  if (frame.format() == media::PIXEL_FORMAT_I420A)
+    return false;
+
+  if (frame.HasTextures())
+    return true;
+
+  if (frame.format() == media::PIXEL_FORMAT_ARGB ||
+      frame.format() == media::PIXEL_FORMAT_XRGB ||
+      frame.format() == media::PIXEL_FORMAT_ABGR ||
+      frame.format() == media::PIXEL_FORMAT_XBGR) {
+    return false;
+  }
+
+  constexpr int kCpuEfficientFrameSize = 320u * 240u;
+  return frame.visible_rect().size().GetArea() > kCpuEfficientFrameSize;
+}
+
+scoped_refptr<StaticBitmapImage> CreateImage(
+    scoped_refptr<media::VideoFrame> frame) {
+  // TODO(sandersd): Do we need to be able to handle limited-range RGB? It
+  // may never happen, and SkColorSpace doesn't know about it.
+  auto sk_color_space =
+      frame->ColorSpace().GetAsFullRangeRGB().ToSkColorSpace();
+  if (!sk_color_space)
+    sk_color_space = SkColorSpace::MakeSRGB();
+
+  if (CanUseZeroCopyImages(*frame)) {
+    const SkImageInfo sk_image_info = SkImageInfo::Make(
+        frame->coded_size().width(), frame->coded_size().height(),
+        kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
+
+    // Hold a ref by storing it in the release callback.
+    auto release_callback = viz::SingleReleaseCallback::Create(
+        WTF::Bind([](scoped_refptr<media::VideoFrame> frame,
+                     const gpu::SyncToken& sync_token, bool is_lost) {},
+                  frame));
+
+    return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+        frame->mailbox_holder(0).mailbox, frame->mailbox_holder(0).sync_token,
+        0u, sk_image_info, frame->mailbox_holder(0).texture_target, true,
+        // Pass nullptr for |context_provider_wrapper|, because we don't
+        // know which context the mailbox came from. It is used only to
+        // detect when the mailbox is invalid due to context loss, and is
+        // ignored when |is_cross_thread|.
+        base::WeakPtr<WebGraphicsContext3DProviderWrapper>(),
+        // Pass null |context_thread_ref|, again because we don't know
+        // which context the mailbox came from. This should always trigger
+        // |is_cross_thread|.
+        base::PlatformThreadRef(),
+        // The task runner is only used for |release_callback|.
+        Thread::Current()->GetTaskRunner(), std::move(release_callback));
+  }
+
+  const bool is_mappable =
+      frame->IsMappable() && (frame->format() == media::PIXEL_FORMAT_I420 ||
+                              frame->format() == media::PIXEL_FORMAT_I420A);
+  const bool is_texturable =
+      frame->HasTextures() && (frame->format() == media::PIXEL_FORMAT_I420 ||
+                               frame->format() == media::PIXEL_FORMAT_I420A ||
+                               frame->format() == media::PIXEL_FORMAT_NV12);
+  const bool is_rgb = frame->format() == media::PIXEL_FORMAT_ARGB ||
+                      frame->format() == media::PIXEL_FORMAT_XRGB ||
+                      frame->format() == media::PIXEL_FORMAT_ABGR ||
+                      frame->format() == media::PIXEL_FORMAT_XBGR;
+
+  if (!is_mappable && !is_texturable && !is_rgb) {
+    DLOG(ERROR) << "Unsupported VideoFrame: " << frame->AsHumanReadableString();
+    return nullptr;
+  }
+
+  if (!PreferAcceleratedImages(*frame)) {
+    auto info = SkImageInfo::Make(
+        frame->visible_rect().width(), frame->visible_rect().height(),
+        kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
+
+    sk_sp<SkData> image_pixels = TryAllocateSkData(info.computeMinByteSize());
+    if (!image_pixels)
+      return nullptr;
+
+    media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
+        frame.get(), image_pixels->writable_data(), info.minRowBytes());
+    return UnacceleratedStaticBitmapImage::Create(SkImage::MakeRasterData(
+        info, std::move(image_pixels), info.minRowBytes()));
+  }
+
+  auto raster_context_provider = GetRasterContextProvider();
+  if (!raster_context_provider) {
+    DLOG(ERROR) << "Graphics context unavailable.";
+    return nullptr;
+  }
+
+  auto* ri = raster_context_provider->RasterInterface();
+  auto* shared_image_interface =
+      raster_context_provider->SharedImageInterface();
+  uint32_t usage =
+      gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_DISPLAY;
+  if (raster_context_provider->ContextCapabilities().supports_oop_raster) {
+    usage |= gpu::SHARED_IMAGE_USAGE_RASTER |
+             gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
+  }
+
+  gpu::MailboxHolder dest_holder;
+  // Use coded_size() to comply with media::ConvertFromVideoFrameYUV.
+  dest_holder.mailbox = shared_image_interface->CreateSharedImage(
+      viz::ResourceFormat::RGBA_8888, frame->coded_size(), gfx::ColorSpace(),
+      kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
+      gpu::kNullSurfaceHandle);
+  dest_holder.sync_token = shared_image_interface->GenUnverifiedSyncToken();
+  dest_holder.texture_target = GL_TEXTURE_2D;
+
+  if (frame->NumTextures() == 1) {
+    ri->WaitSyncTokenCHROMIUM(dest_holder.sync_token.GetConstData());
+    ri->CopySubTexture(frame->mailbox_holder(0).mailbox, dest_holder.mailbox,
+                       GL_TEXTURE_2D, 0, 0, 0, 0, frame->coded_size().width(),
+                       frame->coded_size().height(), GL_FALSE, GL_FALSE);
+  } else {
+    media::VideoFrameYUVConverter::ConvertYUVVideoFrameNoCaching(
+        frame.get(), raster_context_provider.get(), dest_holder);
+  }
+
+  gpu::SyncToken sync_token;
+  ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+
+  auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
+      [](scoped_refptr<viz::RasterContextProvider> provider,
+         gpu::Mailbox mailbox, const gpu::SyncToken& sync_token, bool is_lost) {
+        provider->SharedImageInterface()->DestroySharedImage(sync_token,
+                                                             mailbox);
+      },
+      raster_context_provider, dest_holder.mailbox));
+
+  const auto sk_image_info = SkImageInfo::Make(
+      frame->coded_size().width(), frame->coded_size().height(),
+      kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
+
+  auto image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+      dest_holder.mailbox, sync_token, 0u, sk_image_info,
+      dest_holder.texture_target, true,
+      SharedGpuContext::ContextProviderWrapper(),
+      base::PlatformThread::CurrentRef(), Thread::Current()->GetTaskRunner(),
+      std::move(release_callback));
+
+  if (frame->HasTextures()) {
+    // Attach a new sync token to |frame|, so it's not destroyed
+    // before |image| is fully created.
+    media::WaitAndReplaceSyncTokenClient client(ri);
+    frame->UpdateReleaseSyncToken(&client);
+  }
+  return image;
+}
+
+bool IsSupportedPlanarFormat(const media::VideoFrame& frame) {
+  if (!frame.IsMappable() && !frame.HasGpuMemoryBuffer())
+    return false;
+
+  const size_t num_planes = frame.layout().num_planes();
+  switch (frame.format()) {
+    case media::PIXEL_FORMAT_I420:
+      return num_planes == 3;
+    case media::PIXEL_FORMAT_I420A:
+      return num_planes == 4;
+    case media::PIXEL_FORMAT_NV12:
+      return num_planes == 2;
+    case media::PIXEL_FORMAT_XBGR:
+    case media::PIXEL_FORMAT_XRGB:
+    case media::PIXEL_FORMAT_ABGR:
+    case media::PIXEL_FORMAT_ARGB:
+      return num_planes == 1;
+    default:
+      return false;
+  }
+}
+
 }  // namespace
 
 VideoFrame::VideoFrame(scoped_refptr<media::VideoFrame> frame,
-                       ExecutionContext* context)
-    : handle_(
-          base::MakeRefCounted<VideoFrameHandle>(std::move(frame), context)) {
-  DCHECK(handle_->frame());
+                       ExecutionContext* context) {
+  DCHECK(frame);
+  handle_ = base::MakeRefCounted<VideoFrameHandle>(std::move(frame), context);
 }
 
 VideoFrame::VideoFrame(scoped_refptr<VideoFrameHandle> handle)
     : handle_(std::move(handle)) {
   DCHECK(handle_);
+
+  // Note: The provided |handle| may be invalid if close() has been called while
+  // a frame is in transit to another thread.
 }
 
 // static
@@ -234,7 +423,9 @@
   auto sk_color_space = sk_image_info.refColorSpace();
   if (!sk_color_space)
     sk_color_space = SkColorSpace::MakeSRGB();
-  if (!IsValidSkColorSpace(sk_color_space)) {
+
+  const auto gfx_color_space = gfx::ColorSpace(*sk_color_space);
+  if (!gfx_color_space.IsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid color space");
     return nullptr;
@@ -274,6 +465,7 @@
     }
 
     frame = std::move(result.frame);
+    frame->set_color_space(gfx_color_space);
     return MakeGarbageCollected<VideoFrame>(
         std::move(frame), ExecutionContext::From(script_state));
   }
@@ -285,7 +477,7 @@
                                       "Failed to create video frame");
     return nullptr;
   }
-  frame->set_color_space(gfx::ColorSpace(*sk_color_space));
+  frame->set_color_space(gfx_color_space);
   return MakeGarbageCollected<VideoFrame>(
       base::MakeRefCounted<VideoFrameHandle>(
           std::move(frame), std::move(sk_image),
@@ -463,35 +655,9 @@
                                           ExecutionContext::From(script_state));
 }
 
-// static
-bool VideoFrame::IsSupportedPlanarFormat(media::VideoFrame* frame) {
-  if (!frame)
-    return false;
-
-  if (!frame->IsMappable() && !frame->HasGpuMemoryBuffer())
-    return false;
-
-  const size_t num_planes = frame->layout().num_planes();
-  switch (frame->format()) {
-    case media::PIXEL_FORMAT_I420:
-      return num_planes == 3;
-    case media::PIXEL_FORMAT_I420A:
-      return num_planes == 4;
-    case media::PIXEL_FORMAT_NV12:
-      return num_planes == 2;
-    case media::PIXEL_FORMAT_XBGR:
-    case media::PIXEL_FORMAT_XRGB:
-    case media::PIXEL_FORMAT_ABGR:
-    case media::PIXEL_FORMAT_ARGB:
-      return num_planes == 1;
-    default:
-      return false;
-  }
-}
-
 String VideoFrame::format() const {
   auto local_frame = handle_->frame();
-  if (!local_frame || !IsSupportedPlanarFormat(local_frame.get()))
+  if (!local_frame || !IsSupportedPlanarFormat(*local_frame))
     return String();
 
   switch (local_frame->format()) {
@@ -518,7 +684,7 @@
   // Verify that |this| has not been invalidated, and that the format is
   // supported.
   auto local_frame = handle_->frame();
-  if (!local_frame || !IsSupportedPlanarFormat(local_frame.get()))
+  if (!local_frame || !IsSupportedPlanarFormat(*local_frame))
     return base::nullopt;
 
   // Create a Plane for each VideoFrame plane, but only the first time.
@@ -604,7 +770,6 @@
 }
 
 void VideoFrame::close() {
-  // TODO(tguilbert): Add a warning when closing already closed frames?
   handle_->Invalidate();
 }
 
@@ -635,23 +800,10 @@
   return handle ? MakeGarbageCollected<VideoFrame>(std::move(handle)) : nullptr;
 }
 
-scoped_refptr<VideoFrameHandle> VideoFrame::handle() {
-  return handle_;
-}
-
-scoped_refptr<media::VideoFrame> VideoFrame::frame() {
-  return handle_->frame();
-}
-
-scoped_refptr<const media::VideoFrame> VideoFrame::frame() const {
-  return handle_->frame();
-}
-
 ScriptPromise VideoFrame::createImageBitmap(ScriptState* script_state,
                                             const ImageBitmapOptions* options,
                                             ExceptionState& exception_state) {
   base::Optional<IntRect> crop_rect;
-
   if (auto local_frame = handle_->frame())
     crop_rect = IntRect(local_frame->visible_rect());
 
@@ -661,13 +813,23 @@
 
 IntSize VideoFrame::BitmapSourceSize() const {
   // TODO(crbug.com/1096724): Should be scaled to display size.
-  return IntSize(cropWidth(), cropHeight());
+  if (auto local_frame = handle_->frame())
+    return IntSize(local_frame->visible_rect().size());
+  return IntSize();
 }
 
 ScriptPromise VideoFrame::CreateImageBitmap(ScriptState* script_state,
                                             base::Optional<IntRect> crop_rect,
                                             const ImageBitmapOptions* options,
                                             ExceptionState& exception_state) {
+  const auto& frame = handle_->frame();
+  if (!frame) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Cannot create ImageBitmap from destroyed VideoFrame.");
+    return ScriptPromise();
+  }
+
   if (auto sk_img = handle_->sk_image()) {
     auto* image_bitmap = MakeGarbageCollected<ImageBitmap>(
         UnacceleratedStaticBitmapImage::Create(std::move(sk_img)), crop_rect,
@@ -676,199 +838,19 @@
                                                  exception_state);
   }
 
-  auto local_frame = frame();
-  if (!local_frame) {
+  const auto image = CreateImage(frame);
+  if (!image) {
     exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "Cannot create ImageBitmap from destroyed VideoFrame.");
+        DOMExceptionCode::kNotSupportedError,
+        String(("Unsupported VideoFrame: " + frame->AsHumanReadableString())
+                   .c_str()));
     return ScriptPromise();
   }
 
-  // SharedImage optimization: create AcceleratedStaticBitmapImage directly.
-  // Disabled on Android because the hardware decode implementation may neuter
-  // frames, which would violate ImageBitmap requirements.
-  // TODO(sandersd): Handle YUV pixel formats.
-  // TODO(sandersd): Handle high bit depth formats.
-#if !defined(OS_ANDROID)
-  if (local_frame->NumTextures() == 1 &&
-      local_frame->mailbox_holder(0).mailbox.IsSharedImage() &&
-      (local_frame->format() == media::PIXEL_FORMAT_ARGB ||
-       local_frame->format() == media::PIXEL_FORMAT_XRGB ||
-       local_frame->format() == media::PIXEL_FORMAT_ABGR ||
-       local_frame->format() == media::PIXEL_FORMAT_XBGR ||
-       local_frame->format() == media::PIXEL_FORMAT_BGRA)) {
-    // TODO(sandersd): Do we need to be able to handle limited-range RGB? It
-    // may never happen, and SkColorSpace doesn't know about it.
-    auto sk_color_space =
-        local_frame->ColorSpace().GetAsFullRangeRGB().ToSkColorSpace();
-    if (!sk_color_space)
-      sk_color_space = SkColorSpace::MakeSRGB();
-
-    const SkImageInfo sk_image_info = SkImageInfo::Make(
-        local_frame->coded_size().width(), local_frame->coded_size().height(),
-        kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
-
-    // Hold a ref by storing it in the release callback.
-    auto release_callback = viz::SingleReleaseCallback::Create(
-        WTF::Bind([](scoped_refptr<media::VideoFrame> frame,
-                     const gpu::SyncToken& sync_token, bool is_lost) {},
-                  local_frame));
-
-    scoped_refptr<StaticBitmapImage> image =
-        AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
-            local_frame->mailbox_holder(0).mailbox,
-            local_frame->mailbox_holder(0).sync_token, 0u, sk_image_info,
-            local_frame->mailbox_holder(0).texture_target, true,
-            // Pass nullptr for |context_provider_wrapper|, because we don't
-            // know which context the mailbox came from. It is used only to
-            // detect when the mailbox is invalid due to context loss, and is
-            // ignored when |is_cross_thread|.
-            base::WeakPtr<WebGraphicsContext3DProviderWrapper>(),
-            // Pass null |context_thread_ref|, again because we don't know
-            // which context the mailbox came from. This should always trigger
-            // |is_cross_thread|.
-            base::PlatformThreadRef(),
-            // The task runner is only used for |release_callback|.
-            Thread::Current()->GetTaskRunner(), std::move(release_callback));
-    ImageBitmap* image_bitmap =
-        MakeGarbageCollected<ImageBitmap>(image, crop_rect, options);
-    return ImageBitmapSource::FulfillImageBitmap(script_state, image_bitmap,
-                                                 exception_state);
-  }
-#endif  // !defined(OS_ANDROID)
-
-  const bool is_rgb = local_frame->format() == media::PIXEL_FORMAT_ARGB ||
-                      local_frame->format() == media::PIXEL_FORMAT_XRGB ||
-                      local_frame->format() == media::PIXEL_FORMAT_ABGR ||
-                      local_frame->format() == media::PIXEL_FORMAT_XBGR;
-
-  if ((local_frame->IsMappable() &&
-       (local_frame->format() == media::PIXEL_FORMAT_I420 ||
-        local_frame->format() == media::PIXEL_FORMAT_I420A)) ||
-      (local_frame->HasTextures() &&
-       (local_frame->format() == media::PIXEL_FORMAT_I420 ||
-        local_frame->format() == media::PIXEL_FORMAT_I420A ||
-        local_frame->format() == media::PIXEL_FORMAT_NV12)) ||
-      is_rgb) {
-    scoped_refptr<StaticBitmapImage> image;
-    gfx::ColorSpace gfx_color_space = local_frame->ColorSpace();
-    gfx_color_space = gfx_color_space.GetWithMatrixAndRange(
-        gfx::ColorSpace::MatrixID::RGB, gfx::ColorSpace::RangeID::FULL);
-    auto sk_color_space = gfx_color_space.ToSkColorSpace();
-    if (!sk_color_space)
-      sk_color_space = SkColorSpace::MakeSRGB();
-
-    const bool prefer_accelerated_image_bitmap =
-        local_frame->format() != media::PIXEL_FORMAT_I420A &&
-        (BitmapSourceSize().Area() > kCpuEfficientFrameSize ||
-         local_frame->HasTextures()) &&
-        (!is_rgb || local_frame->HasTextures());
-
-    if (!prefer_accelerated_image_bitmap) {
-      size_t bytes_per_row = sizeof(SkColor) * cropWidth();
-      size_t image_pixels_size = bytes_per_row * cropHeight();
-
-      sk_sp<SkData> image_pixels = TryAllocateSkData(image_pixels_size);
-      if (!image_pixels) {
-        exception_state.ThrowDOMException(DOMExceptionCode::kBufferOverrunError,
-                                          "Out of memory.");
-        return ScriptPromise();
-      }
-      media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
-          local_frame.get(), image_pixels->writable_data(), bytes_per_row);
-
-      SkImageInfo info =
-          SkImageInfo::Make(cropWidth(), cropHeight(), kN32_SkColorType,
-                            kUnpremul_SkAlphaType, std::move(sk_color_space));
-      sk_sp<SkImage> skImage =
-          SkImage::MakeRasterData(info, image_pixels, bytes_per_row);
-      image = UnacceleratedStaticBitmapImage::Create(std::move(skImage));
-    } else {
-      scoped_refptr<viz::RasterContextProvider> raster_context_provider;
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper> wrapper =
-          SharedGpuContext::ContextProviderWrapper();
-      if (wrapper && wrapper->ContextProvider()) {
-        raster_context_provider = base::WrapRefCounted(
-            wrapper->ContextProvider()->RasterContextProvider());
-      }
-      if (!raster_context_provider) {
-        exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
-                                          "Graphics context unavailable.");
-        return ScriptPromise();
-      }
-
-      auto* ri = raster_context_provider->RasterInterface();
-      gpu::SharedImageInterface* shared_image_interface =
-          raster_context_provider->SharedImageInterface();
-      uint32_t usage =
-          gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_DISPLAY;
-      if (raster_context_provider->ContextCapabilities().supports_oop_raster) {
-        usage |= gpu::SHARED_IMAGE_USAGE_RASTER |
-                 gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-      }
-
-      gpu::MailboxHolder dest_holder;
-      // Use coded_size() to comply with media::ConvertFromVideoFrameYUV.
-      dest_holder.mailbox = shared_image_interface->CreateSharedImage(
-          viz::ResourceFormat::RGBA_8888, local_frame->coded_size(),
-          gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
-          usage, gpu::kNullSurfaceHandle);
-      dest_holder.sync_token = shared_image_interface->GenUnverifiedSyncToken();
-      dest_holder.texture_target = GL_TEXTURE_2D;
-
-      if (local_frame->NumTextures() == 1) {
-        ri->WaitSyncTokenCHROMIUM(dest_holder.sync_token.GetConstData());
-        ri->CopySubTexture(
-            local_frame->mailbox_holder(0).mailbox, dest_holder.mailbox,
-            GL_TEXTURE_2D, 0, 0, 0, 0, local_frame->coded_size().width(),
-            local_frame->coded_size().height(), GL_FALSE, GL_FALSE);
-      } else {
-        media::VideoFrameYUVConverter::ConvertYUVVideoFrameNoCaching(
-            local_frame.get(), raster_context_provider.get(), dest_holder);
-      }
-
-      gpu::SyncToken sync_token;
-      ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-
-      auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
-          [](scoped_refptr<viz::RasterContextProvider> provider,
-             gpu::Mailbox mailbox, const gpu::SyncToken& sync_token,
-             bool is_lost) {
-            provider->SharedImageInterface()->DestroySharedImage(sync_token,
-                                                                 mailbox);
-          },
-          raster_context_provider, dest_holder.mailbox));
-
-      const SkImageInfo sk_image_info =
-          SkImageInfo::Make(codedWidth(), codedHeight(), kN32_SkColorType,
-                            kUnpremul_SkAlphaType, std::move(sk_color_space));
-
-      image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
-          dest_holder.mailbox, sync_token, 0u, sk_image_info,
-          dest_holder.texture_target, true,
-          SharedGpuContext::ContextProviderWrapper(),
-          base::PlatformThread::CurrentRef(),
-          Thread::Current()->GetTaskRunner(), std::move(release_callback));
-
-      if (local_frame->HasTextures()) {
-        // Attach a new sync token to |local_frame|, so it's not destroyed
-        // before |image| is fully created.
-        media::WaitAndReplaceSyncTokenClient client(ri);
-        local_frame->UpdateReleaseSyncToken(&client);
-      }
-    }
-
-    ImageBitmap* image_bitmap =
-        MakeGarbageCollected<ImageBitmap>(image, crop_rect, options);
-    return ImageBitmapSource::FulfillImageBitmap(script_state, image_bitmap,
-                                                 exception_state);
-  }
-
-  exception_state.ThrowDOMException(
-      DOMExceptionCode::kNotSupportedError,
-      String(("Unsupported VideoFrame: " + local_frame->AsHumanReadableString())
-                 .c_str()));
-  return ScriptPromise();
+  auto* image_bitmap =
+      MakeGarbageCollected<ImageBitmap>(image, crop_rect, options);
+  return ImageBitmapSource::FulfillImageBitmap(script_state, image_bitmap,
+                                               exception_state);
 }
 
 void VideoFrame::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.h b/third_party/blink/renderer/modules/webcodecs/video_frame.h
index b8421b9..b3eb3d6 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include "base/optional.h"
-#include "media/base/video_frame.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap_source.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webcodecs/plane.h"
@@ -18,6 +17,13 @@
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
+// Note: Don't include "media/base/video_frame.h" here without good reason,
+// since it includes a lot of non-blink types which can pollute the namespace.
+
+namespace media {
+class VideoFrame;
+}
+
 namespace blink {
 
 class ImageBitmap;
@@ -86,18 +92,14 @@
                                   const ImageBitmapOptions*,
                                   ExceptionState&);
 
-  scoped_refptr<VideoFrameHandle> handle();
-
   // Convenience functions
-  scoped_refptr<media::VideoFrame> frame();
-  scoped_refptr<const media::VideoFrame> frame() const;
+  scoped_refptr<VideoFrameHandle> handle() const { return handle_; }
+  scoped_refptr<media::VideoFrame> frame() const { return handle_->frame(); }
 
   // GarbageCollected override
   void Trace(Visitor*) const override;
 
  private:
-  static bool IsSupportedPlanarFormat(media::VideoFrame*);
-
   // ImageBitmapSource implementation
   static constexpr uint64_t kCpuEfficientFrameSize = 320u * 240u;
   IntSize BitmapSourceSize() const override;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
index 9126f803..3d0cb3c 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/webcodecs/video_frame_handle.h"
 
+#include "media/base/video_frame.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/skia/include/core/SkImage.h"
 
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
index bbbb120..a5b1130 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
@@ -6,15 +6,21 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_VIDEO_FRAME_HANDLE_H_
 
 #include "base/memory/scoped_refptr.h"
-#include "media/base/video_frame.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame_logger.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
+// Note: Don't include "media/base/video_frame.h" here without good reason,
+// since it includes a lot of non-blink types which can pollute the namespace.
+
 class SkImage;
 
+namespace media {
+class VideoFrame;
+}
+
 namespace blink {
 
 class ExecutionContext;
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
index 3dd420f9..8302820 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -833,7 +833,8 @@
 }
 
 WGPUExtent3D AsDawnType(
-    const UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict* webgpu_extent) {
+    const UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict* webgpu_extent,
+    GPUDevice* device) {
   DCHECK(webgpu_extent);
 
   WGPUExtent3D dawn_extent = {1, 1, 1};
@@ -863,7 +864,14 @@
         webgpu_extent->GetAsGPUExtent3DDict();
     dawn_extent.width = webgpu_extent_3d_dict->width();
     dawn_extent.height = webgpu_extent_3d_dict->height();
-    dawn_extent.depth = webgpu_extent_3d_dict->depth();
+
+    if (webgpu_extent_3d_dict->hasDepth()) {
+      device->AddConsoleWarning(
+          "Specifying an extent depth is deprecated. Use depthOrArrayLayers.");
+      dawn_extent.depth = webgpu_extent_3d_dict->depth();
+    } else {
+      dawn_extent.depth = webgpu_extent_3d_dict->depthOrArrayLayers();
+    }
 
   } else {
     NOTREACHED();
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.h b/third_party/blink/renderer/modules/webgpu/dawn_conversions.h
index b4a688f53..56dddcb2 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.h
@@ -46,7 +46,8 @@
 WGPUColor AsDawnType(const GPUColorDict*);
 WGPUColor AsDawnType(const DoubleSequenceOrGPUColorDict*);
 WGPUExtent3D AsDawnType(
-    const UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict*);
+    const UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict*,
+    GPUDevice* device);
 WGPUOrigin3D AsDawnType(
     const UnsignedLongEnforceRangeSequenceOrGPUOrigin3DDict*);
 WGPUTextureCopyView AsDawnType(const GPUTextureCopyView* webgpu_view,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
index f2df593..283a725 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -246,7 +246,7 @@
     GPUBufferCopyView* source,
     GPUTextureCopyView* destination,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size) {
-  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size);
+  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size, device_);
   WGPUTextureCopyView dawn_destination = AsDawnType(destination, device_);
 
   const char* error = nullptr;
@@ -265,7 +265,7 @@
     GPUTextureCopyView* source,
     GPUBufferCopyView* destination,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size) {
-  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size);
+  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size, device_);
   WGPUTextureCopyView dawn_source = AsDawnType(source, device_);
 
   const char* error = nullptr;
@@ -286,7 +286,7 @@
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size) {
   WGPUTextureCopyView dawn_source = AsDawnType(source, device_);
   WGPUTextureCopyView dawn_destination = AsDawnType(destination, device_);
-  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size);
+  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size, device_);
 
   GetProcs().commandEncoderCopyTextureToTexture(
       GetHandle(), &dawn_source, &dawn_destination, &dawn_copy_size);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl b/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
index 6fc1fe24..7c67936 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
@@ -7,5 +7,7 @@
 dictionary GPUExtent3DDict {
     GPUIntegerCoordinate width = 1;
     GPUIntegerCoordinate height = 1;
-    GPUIntegerCoordinate depth = 1;
+    GPUIntegerCoordinate depthOrArrayLayers = 1;
+    // Deprecated
+    GPUIntegerCoordinate depth;
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index 5b4e02e4..b42abb8 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -292,7 +292,7 @@
     GPUTextureDataLayout* data_layout,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& write_size,
     ExceptionState& exception_state) {
-  WGPUExtent3D dawn_write_size = AsDawnType(&write_size);
+  WGPUExtent3D dawn_write_size = AsDawnType(&write_size, device_);
   WGPUTextureCopyView dawn_destination = AsDawnType(destination, device_);
 
   WGPUTextureDataLayout dawn_data_layout = {};
@@ -335,7 +335,7 @@
   // appropriate format. Now only support texture format exactly the same. The
   // compatible formats need to be defined in WebGPU spec.
 
-  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size);
+  WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size, device_);
 
   // Extract imageBitmap attributes
   WGPUOrigin3D origin_in_image_bitmap =
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
index 2ab5110..b2590bcb 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
@@ -42,7 +42,7 @@
   dawn_desc.usage = static_cast<WGPUTextureUsage>(webgpu_desc->usage());
   dawn_desc.dimension =
       AsDawnEnum<WGPUTextureDimension>(webgpu_desc->dimension());
-  dawn_desc.size = AsDawnType(&webgpu_desc->size());
+  dawn_desc.size = AsDawnType(&webgpu_desc->size(), device);
   dawn_desc.format = AsDawnEnum<WGPUTextureFormat>(webgpu_desc->format());
   dawn_desc.mipLevelCount = webgpu_desc->mipLevelCount();
   dawn_desc.sampleCount = webgpu_desc->sampleCount();
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
index c02306c..c12edb7 100644
--- a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
@@ -4,11 +4,14 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h"
 
+#include <cmath>
 #include <cstdlib>
 
 #include "base/numerics/checked_math.h"
+#include "base/numerics/ranges.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "ui/gfx/geometry/point3_f.h"
 
 namespace {
 constexpr char kOutOfBoundsAccess[] =
@@ -44,35 +47,52 @@
 }
 
 float XRCPUDepthInformation::getDepthInMeters(
-    uint32_t column,
-    uint32_t row,
+    float x,
+    float y,
     ExceptionState& exception_state) const {
-  DVLOG(3) << __func__ << ": column=" << column << ", row=" << row;
+  DVLOG(3) << __func__ << ": x=" << x << ", y=" << y;
 
   if (!ValidateFrame(exception_state)) {
     return 0.0;
   }
 
-  if (column >= static_cast<size_t>(size_.width())) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
-                                      kOutOfBoundsAccess);
+  if (x > 1.0 || x < 0.0) {
+    exception_state.ThrowRangeError(kOutOfBoundsAccess);
     return 0.0;
   }
 
-  if (row >= static_cast<size_t>(size_.height())) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
-                                      kOutOfBoundsAccess);
+  if (y > 1.0 || y < 0.0) {
+    exception_state.ThrowRangeError(kOutOfBoundsAccess);
     return 0.0;
   }
 
+  // Those coordinates are actually `norm_view_coordinates` before a series of
+  // transforms is applied, but they are modified in-place, so the name's in
+  // anticipation of those transforms.
+  gfx::Point3F depth_coordinates(x, y, 0.0);
+
+  // `norm_view_coordinates` becomes `norm_depth_coordinates`:
+  norm_depth_buffer_from_norm_view_.TransformPoint(&depth_coordinates);
+
+  // `norm_depth_coordinates` becomes `depth_coordinates`:
+  depth_coordinates.Scale(size_.width(), size_.height(), 1.0);
+
+  uint32_t column = base::ClampToRange<uint32_t>(
+      static_cast<uint32_t>(std::round(depth_coordinates.x())), 0,
+      size_.width() - 1);
+  uint32_t row = base::ClampToRange<uint32_t>(
+      static_cast<uint32_t>(std::round(depth_coordinates.y())), 0,
+      size_.height() - 1);
+
   auto checked_index =
       base::CheckAdd(column, base::CheckMul(row, size_.width()));
   size_t index = checked_index.ValueOrDie();
 
   // Convert from data's native units to meters when accessing:
-  float result = data_->Item(index) * rawValueToMeters_;
+  float result = data_->Item(index) * raw_value_to_meters_;
 
-  DVLOG(3) << __func__ << ": index=" << index << ", result=" << result;
+  DVLOG(3) << __func__ << ": x=" << x << ", y=" << y << ", column=" << column
+           << ", row=" << row << ", index=" << index << ", result=" << result;
 
   return result;
 }
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h
index 790918c..312311e 100644
--- a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h
@@ -31,8 +31,8 @@
 
   DOMUint16Array* data(ExceptionState& exception_state) const;
 
-  float getDepthInMeters(uint32_t column,
-                         uint32_t row,
+  float getDepthInMeters(float x,
+                         float y,
                          ExceptionState& exception_state) const;
 
   void Trace(Visitor* visitor) const override;
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl
index b2a98d2..84dccdd7e4 100644
--- a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl
@@ -7,10 +7,14 @@
     Exposed=Window,
     RuntimeEnabled=WebXRDepth
 ] interface XRCPUDepthInformation : XRDepthInformation {
-  [RaisesException, SameObject, MeasureAs=XRCPUDepthInformationDataAttribute]
-  readonly attribute DataView data;
+  [
+    RaisesException,
+    SameObject,
+    SaveSameObject,
+    MeasureAs=XRCPUDepthInformationDataAttribute
+  ] readonly attribute DataView data;
 
   [RaisesException, MeasureAs=XRCPUDepthInformationGetDepth]
-  float getDepthInMeters(unsigned long column, unsigned long row);
+  float getDepthInMeters(float x, float y);
 };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_depth_information.cc
index e55237c..053b030 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.cc
@@ -27,51 +27,33 @@
 XRDepthInformation::XRDepthInformation(
     const XRFrame* xr_frame,
     const gfx::Size& size,
-    const gfx::Transform& norm_texture_from_norm_view,
+    const gfx::Transform& norm_depth_buffer_from_norm_view,
     float raw_value_to_meters)
     : xr_frame_(xr_frame),
       size_(size),
-      norm_texture_from_norm_view_(norm_texture_from_norm_view),
-      rawValueToMeters_(raw_value_to_meters) {
+      norm_depth_buffer_from_norm_view_(norm_depth_buffer_from_norm_view),
+      raw_value_to_meters_(raw_value_to_meters) {
   DVLOG(3) << __func__ << ": size_=" << size_.ToString()
-           << ", norm_texture_from_norm_view_="
-           << norm_texture_from_norm_view_.ToString()
-           << ", raw_value_to_meters=" << raw_value_to_meters;
+           << ", norm_depth_buffer_from_norm_view_="
+           << norm_depth_buffer_from_norm_view_.ToString()
+           << ", raw_value_to_meters_=" << raw_value_to_meters_;
 }
 
-uint32_t XRDepthInformation::width(ExceptionState& exception_state) const {
-  if (!ValidateFrame(exception_state)) {
-    return 0;
-  }
-
+uint32_t XRDepthInformation::width() const {
   return size_.width();
 }
 
-uint32_t XRDepthInformation::height(ExceptionState& exception_state) const {
-  if (!ValidateFrame(exception_state)) {
-    return 0;
-  }
-
+uint32_t XRDepthInformation::height() const {
   return size_.height();
 }
 
-float XRDepthInformation::rawValueToMeters(
-    ExceptionState& exception_state) const {
-  if (!ValidateFrame(exception_state)) {
-    return 0.0;
-  }
-
-  return rawValueToMeters_;
+float XRDepthInformation::rawValueToMeters() const {
+  return raw_value_to_meters_;
 }
 
-XRRigidTransform* XRDepthInformation::normTextureFromNormView(
-    ExceptionState& exception_state) const {
-  if (!ValidateFrame(exception_state)) {
-    return nullptr;
-  }
-
+XRRigidTransform* XRDepthInformation::normDepthBufferFromNormView() const {
   return MakeGarbageCollected<XRRigidTransform>(
-      TransformationMatrix(norm_texture_from_norm_view_.matrix()));
+      TransformationMatrix(norm_depth_buffer_from_norm_view_.matrix()));
 }
 
 bool XRDepthInformation::ValidateFrame(ExceptionState& exception_state) const {
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.h b/third_party/blink/renderer/modules/xr/xr_depth_information.h
index 37388c2..e371581 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.h
@@ -20,10 +20,11 @@
   DEFINE_WRAPPERTYPEINFO();
 
  protected:
-  explicit XRDepthInformation(const XRFrame* xr_frame,
-                              const gfx::Size& size,
-                              const gfx::Transform& norm_texture_from_norm_view,
-                              float raw_value_to_meters);
+  explicit XRDepthInformation(
+      const XRFrame* xr_frame,
+      const gfx::Size& size,
+      const gfx::Transform& norm_depth_buffer_from_norm_view,
+      float raw_value_to_meters);
 
   // Helper to validate whether a frame is in a correct state. Should be invoked
   // before every member access. If the validation returns `false`, it means the
@@ -32,14 +33,13 @@
   bool ValidateFrame(ExceptionState& exception_state) const;
 
  public:
-  uint32_t width(ExceptionState& exception_state) const;
+  uint32_t width() const;
 
-  uint32_t height(ExceptionState& exception_state) const;
+  uint32_t height() const;
 
-  XRRigidTransform* normTextureFromNormView(
-      ExceptionState& exception_state) const;
+  XRRigidTransform* normDepthBufferFromNormView() const;
 
-  float rawValueToMeters(ExceptionState& exception_state) const;
+  float rawValueToMeters() const;
 
   void Trace(Visitor* visitor) const override;
 
@@ -48,8 +48,8 @@
 
   const gfx::Size size_;
 
-  const gfx::Transform norm_texture_from_norm_view_;
-  const float rawValueToMeters_;
+  const gfx::Transform norm_depth_buffer_from_norm_view_;
+  const float raw_value_to_meters_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.idl b/third_party/blink/renderer/modules/xr/xr_depth_information.idl
index dea2323..bdcf6184 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.idl
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.idl
@@ -7,11 +7,10 @@
     Exposed=Window,
     RuntimeEnabled=WebXRDepth
 ] interface XRDepthInformation {
-  [RaisesException] readonly attribute unsigned long width;
-  [RaisesException] readonly attribute unsigned long height;
+  readonly attribute unsigned long width;
+  readonly attribute unsigned long height;
 
-  [RaisesException, SameObject]
-  readonly attribute XRRigidTransform normTextureFromNormView;
-  [RaisesException]
+  [SameObject, SaveSameObject]
+  readonly attribute XRRigidTransform normDepthBufferFromNormView;
   readonly attribute float rawValueToMeters;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
index 2b8d314..30bc4f0 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
+++ b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
@@ -10,6 +10,9 @@
 
 namespace {
 
+constexpr char kInvalidUsageMode[] =
+    "Unable to obtain XRCPUDepthInformation in \"gpu-optimized\" usage mode.";
+
 String UsageToString(device::mojom::XRDepthUsage usage) {
   switch (usage) {
     case device::mojom::XRDepthUsage::kCPUOptimized:
@@ -76,8 +79,15 @@
   }
 }
 
-XRCPUDepthInformation* XRDepthManager::GetDepthInformation(
-    const XRFrame* xr_frame) {
+XRCPUDepthInformation* XRDepthManager::GetCpuDepthInformation(
+    const XRFrame* xr_frame,
+    ExceptionState& exception_state) {
+  if (usage_ != device::mojom::XRDepthUsage::kCPUOptimized) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kInvalidUsageMode);
+    return nullptr;
+  }
+
   if (!depth_data_) {
     return nullptr;
   }
@@ -89,6 +99,19 @@
       depth_data_->raw_value_to_meters, data_);
 }
 
+XRWebGLDepthInformation* XRDepthManager::GetWebGLDepthInformation(
+    const XRFrame* xr_frame,
+    ExceptionState& exception_state) {
+  if (usage_ != device::mojom::XRDepthUsage::kGPUOptimized) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kInvalidUsageMode);
+    return nullptr;
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
 void XRDepthManager::EnsureData() {
   DCHECK(depth_data_);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_manager.h b/third_party/blink/renderer/modules/xr/xr_depth_manager.h
index 9fb6168f..beff7e48 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_manager.h
+++ b/third_party/blink/renderer/modules/xr/xr_depth_manager.h
@@ -12,7 +12,9 @@
 
 namespace blink {
 
+class ExceptionState;
 class XRCPUDepthInformation;
+class XRWebGLDepthInformation;
 class XRFrame;
 class XRSession;
 
@@ -31,7 +33,13 @@
   const String& depthUsage() const { return usage_str_; }
   const String& depthDataFormat() const { return data_format_str_; }
 
-  XRCPUDepthInformation* GetDepthInformation(const XRFrame* xr_frame);
+  XRCPUDepthInformation* GetCpuDepthInformation(
+      const XRFrame* xr_frame,
+      ExceptionState& exception_state);
+
+  XRWebGLDepthInformation* GetWebGLDepthInformation(
+      const XRFrame* xr_frame,
+      ExceptionState& exception_state);
 
   void Trace(Visitor* visitor) const;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.cc b/third_party/blink/renderer/modules/xr/xr_frame.cc
index 9dec672..ec4b2ffe 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame.cc
@@ -22,16 +22,9 @@
 
 namespace {
 
-const char kInactiveFrame[] =
-    "XRFrame access outside the callback that produced it is invalid.";
-
 const char kInvalidView[] =
     "XRView passed in to the method did not originate from current XRFrame.";
 
-const char kNonAnimationFrame[] =
-    "getViewerPose can only be called on XRFrame objects passed to "
-    "XRSession.requestAnimationFrame callbacks.";
-
 const char kSessionMismatch[] = "XRSpace and XRFrame sessions do not match.";
 
 const char kCannotReportPoses[] =
@@ -52,6 +45,9 @@
 
 }  // namespace
 
+constexpr char XRFrame::kInactiveFrame[];
+constexpr char XRFrame::kNonAnimationFrame[];
+
 XRFrame::XRFrame(XRSession* session, bool is_animation_frame)
     : session_(session), is_animation_frame_(is_animation_frame) {}
 
@@ -165,6 +161,12 @@
     ExceptionState& exception_state) const {
   DVLOG(2) << __func__;
 
+  if (!session_->IsFeatureEnabled(device::mojom::XRSessionFeature::DEPTH)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        XRSession::kDepthSensingFeatureNotSupported);
+  }
+
   if (!is_active_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kInactiveFrame);
@@ -183,7 +185,7 @@
     return nullptr;
   }
 
-  return session_->GetDepthInformation(this, exception_state);
+  return session_->GetCpuDepthInformation(this, exception_state);
 }
 
 // Return an XRPose that has a transform of basespace_from_space, while
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.h b/third_party/blink/renderer/modules/xr/xr_frame.h
index da1a8c6..3e32442 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame.h
@@ -45,6 +45,12 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  static constexpr char kInactiveFrame[] =
+      "XRFrame access outside the callback that produced it is invalid.";
+  static constexpr char kNonAnimationFrame[] =
+      "This method can only be called on XRFrame objects passed to "
+      "XRSession.requestAnimationFrame callbacks.";
+
   explicit XRFrame(XRSession* session, bool is_animation_frame = false);
 
   XRSession* session() const { return session_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 1ca67a4..83e853d 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -105,9 +105,6 @@
 const char kEntityTypesNotSpecified[] =
     "No entityTypes specified: the array cannot be empty!";
 
-const char kDepthSensingFeatureNotSupported[] =
-    "Depth sensing feature is not supported by the session.";
-
 const double kDegToRad = M_PI / 180.0;
 
 const float kMinDefaultFramebufferScale = 0.1f;
@@ -246,6 +243,7 @@
 constexpr char XRSession::kNoSpaceSpecified[];
 constexpr char XRSession::kAnchorsFeatureNotSupported[];
 constexpr char XRSession::kPlanesFeatureNotSupported[];
+constexpr char XRSession::kDepthSensingFeatureNotSupported[];
 
 class XRSession::XRSessionResizeObserverDelegate final
     : public ResizeObserver::Delegate {
@@ -1248,7 +1246,7 @@
   }
 }
 
-XRCPUDepthInformation* XRSession::GetDepthInformation(
+XRCPUDepthInformation* XRSession::GetCpuDepthInformation(
     const XRFrame* xr_frame,
     ExceptionState& exception_state) const {
   if (!depth_manager_) {
@@ -1257,7 +1255,19 @@
     return nullptr;
   }
 
-  return depth_manager_->GetDepthInformation(xr_frame);
+  return depth_manager_->GetCpuDepthInformation(xr_frame, exception_state);
+}
+
+XRWebGLDepthInformation* XRSession::GetWebGLDepthInformation(
+    const XRFrame* xr_frame,
+    ExceptionState& exception_state) const {
+  if (!depth_manager_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kDepthSensingFeatureNotSupported);
+    return nullptr;
+  }
+
+  return depth_manager_->GetWebGLDepthInformation(xr_frame, exception_state);
 }
 
 ScriptPromise XRSession::requestLightProbe(ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index b9e2a5fc..7483905 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -60,6 +60,7 @@
 class XRTransientInputHitTestOptionsInit;
 class XRTransientInputHitTestSource;
 class XRViewData;
+class XRWebGLDepthInformation;
 class XRWebGLLayer;
 
 using XRSessionFeatureSet = HashSet<device::mojom::XRSessionFeature>;
@@ -83,7 +84,8 @@
       "Anchors feature is not supported by the session.";
   static constexpr char kPlanesFeatureNotSupported[] =
       "Plane detection feature is not supported by the session.";
-
+  static constexpr char kDepthSensingFeatureNotSupported[] =
+      "Depth sensing feature is not supported by the session.";
   // Runs all the video.requestVideoFrameCallback() callbacks associated with
   // one HTMLVideoElement. |double| is the |high_res_now_ms|, derived from
   // MonotonicTimeToZeroBasedDocumentTime(|current_frame_time|), to be passed as
@@ -341,7 +343,11 @@
   base::Optional<TransformationMatrix> GetMojoFrom(
       device::mojom::blink::XRReferenceSpaceType space_type) const;
 
-  XRCPUDepthInformation* GetDepthInformation(
+  XRCPUDepthInformation* GetCpuDepthInformation(
+      const XRFrame* xr_frame,
+      ExceptionState& exception_state) const;
+
+  XRWebGLDepthInformation* GetWebGLDepthInformation(
       const XRFrame* xr_frame,
       ExceptionState& exception_state) const;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
index 56d987b..4277ea8 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
@@ -77,10 +77,27 @@
     return nullptr;
   }
 
+  if (session_->ended()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Cannot get a reflection cube map for a session which has ended.");
+    return nullptr;
+  }
+
+  if (session_ != light_probe->session()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "LightProbe comes from a different session than this binding");
+    return nullptr;
+  }
+
   // Determine the internal_format, format, and type that will be passed to
   // glTexImage2D for each possible light probe reflection format. The formats
   // will differ depending on whether we're using WebGL 2 or WebGL 1 with
   // extensions.
+  // Note that at this point, since we know we have a valid lightProbe, we also
+  // know that we support whatever reflectionFormat it was created with, as it
+  // would not have been created otherwise.
   switch (light_probe->ReflectionFormat()) {
     case XRLightProbe::kReflectionFormatRGBA16F:
       if (!webgl2_ && !webgl_context_->ExtensionsUtil()->IsExtensionEnabled(
@@ -160,7 +177,27 @@
 XRWebGLDepthInformation* XRWebGLBinding::getDepthInformation(
     XRView* view,
     ExceptionState& exception_state) {
-  return nullptr;
+  if (!session_->IsFeatureEnabled(device::mojom::XRSessionFeature::DEPTH)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        XRSession::kDepthSensingFeatureNotSupported);
+  }
+
+  XRFrame* frame = view->frame();
+
+  if (!frame->IsActive()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      XRFrame::kInactiveFrame);
+    return nullptr;
+  }
+
+  if (!frame->IsAnimationFrame()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      XRFrame::kNonAnimationFrame);
+    return nullptr;
+  }
+
+  return view->session()->GetWebGLDepthInformation(frame, exception_state);
 }
 
 void XRWebGLBinding::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc b/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
index 06524fe..bfefbaa 100644
--- a/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
+++ b/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
@@ -321,6 +321,9 @@
       .Record(ukm_recorder_);
   UMA_HISTOGRAM_COUNTS_10000("Blink.Fonts.FontFamilyMatchAttempts.System",
                              system_font_families_.size());
+  UMA_HISTOGRAM_COUNTS_10000(
+      "Blink.Fonts.FontMatchAttempts.System",
+      local_fonts_failed_.size() + local_fonts_succeeded_.size());
 }
 
 void FontMatchingMetrics::OnFontLookup() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
index 0b9a9227..4535a22 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -46,7 +46,9 @@
   if (!provider || !provider->IsAccelerated())
     return nullptr;
 
-  provider->Canvas()->drawImage(paint_image, 0, 0);
+  cc::PaintFlags paint;
+  paint.setBlendMode(SkBlendMode::kSrc);
+  provider->Canvas()->drawImage(paint_image, 0, 0, SkSamplingOptions(), &paint);
   return provider->Snapshot();
 }
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 3de5118..9b0a580 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1022,12 +1022,18 @@
             'third_party/blink/renderer/modules/webcodecs/',
         ],
         'allowed': [
+            'base::PlatformThreadRef',
+            'base::WrapRefCounted',
             'gpu::kNullSurfaceHandle',
             'gpu::SHARED_IMAGE_.+',
             'gpu::raster::RasterInterface',
             'gpu::Mailbox',
             'gpu::MailboxHolder',
-            "viz::RasterContextProvider",
+            'gpu::SharedImageInterface',
+            'gpu::SyncToken',
+            'viz::RasterContextProvider',
+            'viz::ResourceFormat',
+            'viz::SingleReleaseCallback',
             'media::.+',
             'libyuv::.+',
         ]
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index ff23e48..ae78aa2 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -760,7 +760,6 @@
 
 ### Pass in Legacy, fail in NG
 crbug.com/958381 external/wpt/css/css-tables/visibility-collapse-rowspan-005.html [ Pass ]
-crbug.com/958381 fast/table/large-col-span-crash.html [ Pass ]
 crbug.com/958381 fast/table/table-all-rowspans-height-distribution-in-rows-except-overlapped.html [ Pass ]
 crbug.com/958381 fast/table/table-all-rowspans-height-distribution-in-rows.html [ Pass ]
 crbug.com/958381 fast/table/table-rowspan-height-distribution-in-rows-1.html [ Pass ]
@@ -888,6 +887,13 @@
 crbug.com/958381 fast/css/empty-cell-baseline.html [ Failure ]
 crbug.com/958381 fast/css/inline-table-first-row-empty-cell-non-auto.html [ Failure ]
 
+# New failures after TablesNG landed, most likely rebaselined tests
+crbug.com/958381 accessibility/table-column-track-merging.html [ Failure ]
+crbug.com/958381 fast/table/colspanMinWidth.html [ Failure ]
+crbug.com/958381 fast/table/colspanMinWidth-vertical.html [ Failure ]
+crbug.com/958381 fast/table/large-col-span-crash.html [ Failure ]
+crbug.com/958381 external/wpt/css/css-tables/column-track-merging.html [ Failure ]
+
 # TablesNG end
 
 ### compositing/filters/
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fd94ae9..0f91324 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -160,7 +160,6 @@
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-path-context-fill.html [ Failure ]
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-state-persistence-no-dirty.html [ Pass Failure ]
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-transform-skewed.html [ Failure ]
-crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/OffscreenCanvas-Bitmaprenderer-TransferToImageBitmapResetsToBlack.html [ Pass Failure ]
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/OffscreenCanvas-text-FontFace.html [ Pass Failure ]
 
 # Flakey tests
@@ -1064,9 +1063,6 @@
 
 ### TablesNG
 # crbug.com/958381
-# fails because TablesNG does not coalesce columns
-crbug.com/958381 accessibility/table-cells-with-colspan.html [ Failure ]
-crbug.com/958381 fast/table/large-col-span-crash.html [ Failure ]
 
 # TODO fails because cell size with only input element is 18px, not 15. line-height: 0px fixes it.
 crbug.com/1171616 external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ]
@@ -1103,9 +1099,6 @@
 crbug.com/958381 [ Mac ] virtual/layout_ng_block_frag/fragmentation/fragmented-table-cell.html [ Failure ]
 crbug.com/958381 [ Mac ] fragmentation/single-line-cells-paginated-with-text.html [ Failure ]
 
-# Asan underinvalidation failure. Skip while investigating
-crbug.com/958381 paint/tables/huge-table-composited-scroll-collapsed-borders.html [ Skip ]
-
 # TablesNG ends
 
 # ====== LayoutNG-only failures until here ======
@@ -2006,8 +1999,8 @@
 crbug.com/1015331 external/wpt/css/css-text/white-space/eol-spaces-bidi-001.html [ Failure ]
 
 # needs implementation of test_driver_internal.action_sequence
-crbug.com/893480 external/wpt/editing/run/caretnavigation.html [ Timeout ]
-crbug.com/893480 external/wpt/input-events/input-events-typing.html [ Timeout ]
+crbug.com/893480 external/wpt/editing/run/caretnavigation.html [ Failure Timeout ]
+crbug.com/893480 external/wpt/input-events/input-events-typing.html [ Failure Timeout ]
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/eventOrder.html [ Timeout ]
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/elementTiming.html [ Timeout ]
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/multiDevice.html [ Failure Timeout ]
@@ -2019,7 +2012,7 @@
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/textEditCommands.html [ Failure Timeout ]
 crbug.com/893480 external/wpt/input-events/input-events-get-target-ranges.html [ Failure Timeout ]
 crbug.com/893480 external/wpt/input-events/input-events-cut-paste.html [ Failure Timeout ]
-crbug.com/893480 external/wpt/html/semantics/forms/the-input-element/checkable-active-onblur.html [ Timeout ]
+crbug.com/893480 external/wpt/html/semantics/forms/the-input-element/checkable-active-onblur.html [ Failure Timeout ]
 
 # isInputPending requires threaded compositing and layerized iframes
 crbug.com/910421 external/wpt/is-input-pending/* [ Skip ]
@@ -2440,6 +2433,7 @@
 crbug.com/958381 [ Mac ] external/wpt/css/CSS2/tables/table-anonymous-objects-206.xht [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/uievents/keyboard/modifier-keys-combinations.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-flexbox/percentage-descendant-of-anonymous-flex-item.html [ Failure ]
 crbug.com/626703 external/wpt/uievents/keyboard/modifier-keys.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-backgrounds/background-repeat-space-1b.html [ Failure ]
@@ -2597,16 +2591,16 @@
 crbug.com/626703 [ Mac10.13 ] external/wpt/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] virtual/threaded/external/wpt/web-animations/timing-model/animations/updating-the-finished-state.html [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b [ Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b [ Failure Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable [ Failure Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b [ Failure Timeout ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000.html [ Failure ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&child=b [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&child=b [ Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&child=b [ Failure Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i [ Failure Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&child=b [ Failure Timeout ]
 crbug.com/626703 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-001.html [ Failure ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i [ Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable [ Failure Timeout ]
+crbug.com/626703 external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i [ Failure Timeout ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/x-frame-options/multiple.html [ Timeout ]
@@ -2621,14 +2615,14 @@
 crbug.com/626703 external/wpt/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html [ Timeout ]
 crbug.com/626703 external/wpt/mediacapture-record/MediaRecorder-peerconnection.https.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Backspace [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Backspace,ul [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-elements.tentative.html?Backspace [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Delete,ul [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-elements.tentative.html?Delete [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Delete [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Backspace,ol [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Delete,ol [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Backspace [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Backspace,ul [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-elements.tentative.html?Backspace [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Delete,ul [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-elements.tentative.html?Delete [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Delete [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Backspace,ol [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html?Delete,ol [ Failure Timeout ]
 crbug.com/626703 external/wpt/mediacapture-record/MediaRecorder-stop.html [ Timeout ]
 crbug.com/626703 [ Mac11.0 ] external/wpt/fetch/api/response/response-clone.any.worker.html [ Failure Timeout ]
 crbug.com/626703 [ Mac11.0 ] virtual/plz-dedicated-worker/external/wpt/fetch/api/response/response-clone.any.sharedworker.html [ Failure Timeout ]
@@ -2638,7 +2632,7 @@
 crbug.com/626703 [ Mac11.0 ] external/wpt/fetch/api/response/response-clone.any.html [ Failure Timeout ]
 crbug.com/626703 [ Mac11.0 ] virtual/plz-dedicated-worker/external/wpt/fetch/api/response/response-clone.any.html [ Failure Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/webxr/xr_viewport_scale.https.html [ Timeout ]
-crbug.com/626703 external/wpt/editing/other/select-all-and-delete-in-html-element-having-contenteditable.html [ Timeout ]
+crbug.com/626703 external/wpt/editing/other/select-all-and-delete-in-html-element-having-contenteditable.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/paint-timing/with-first-paint/first-contentful-image.html [ Timeout ]
 crbug.com/626703 external/wpt/paint-timing/with-first-paint/mask-image.html [ Timeout ]
 crbug.com/626703 external/wpt/paint-timing/with-first-paint/first-contentful-paint.html [ Timeout ]
@@ -2646,14 +2640,14 @@
 crbug.com/626703 external/wpt/paint-timing/with-first-paint/sibling-painting-first-image.html [ Timeout ]
 crbug.com/626703 external/wpt/paint-timing/with-first-paint/border-image.html [ Timeout ]
 crbug.com/626703 [ Mac10.15 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-local-time-null-2.https.html [ Failure ]
-crbug.com/626703 external/wpt/infrastructure/testdriver/actions/iframe.html [ Timeout ]
+crbug.com/626703 external/wpt/infrastructure/testdriver/actions/iframe.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/infrastructure/testdriver/actions/crossOrigin.sub.html [ Timeout ]
 crbug.com/626703 [ Mac11.0 ] external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-during-and-after-dispatch.tentative.html [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-during-and-after-dispatch.tentative.html [ Failure Timeout ]
 crbug.com/626703 [ Mac11.0 ] external/wpt/scroll-to-text-fragment/redirects.html [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete [ Failure Timeout ]
 crbug.com/626703 external/wpt/screen-capture/feature-policy.https.html [ Timeout ]
 crbug.com/626703 [ Fuchsia ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-pause-immediately.https.html [ Failure ]
 crbug.com/626703 [ Mac11.0 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-pause-immediately.https.html [ Failure ]
@@ -2665,8 +2659,8 @@
 crbug.com/626703 [ Mac10.15 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/valid-image-before-load.https.html [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Failure Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html [ Timeout ]
-crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html [ Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html [ Failure Timeout ]
+crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/streams/transform-streams/patched-global.any.serviceworker.html [ Timeout ]
 crbug.com/626703 external/wpt/streams/transform-streams/patched-global.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/streams/transform-streams/patched-global.any.sharedworker.html [ Timeout ]
@@ -2981,10 +2975,8 @@
 crbug.com/626703 external/wpt/css/selectors/old-tests/css3-modsel-65.xml [ Skip ]
 crbug.com/626703 external/wpt/css/selectors/old-tests/css3-modsel-18b.xml [ Skip ]
 crbug.com/626703 external/wpt/css/selectors/old-tests/css3-modsel-159.xml [ Skip ]
-crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.html [ Timeout ]
 crbug.com/626703 external/wpt/quirks/text-decoration-doesnt-propagate-into-tables/quirks.html [ Failure ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html [ Failure ]
-crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-002.svg [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-001.svg [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-001.svg [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/table-column-track-merging.html b/third_party/blink/web_tests/accessibility/table-column-track-merging.html
new file mode 100644
index 0000000..08e43f3e
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/table-column-track-merging.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<title>Column track merging accesibilty tests</title>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2" />
+<style>
+
+main table {
+  border: 10px solid gray;
+  border-spacing: 20px;
+}
+
+main td {
+  width: 50px;
+  height:50px;
+  padding: 0;
+  background:linear-gradient(to right, yellow, orange);
+}
+main caption {
+  background: #EEE;
+}
+main .desc {
+  margin-top: 20px;
+  color: rgb(50,0,0);
+}
+main pre {
+  white-space: pre-wrap;
+
+}
+</style>
+<main>
+<p>Checks accessibility table properties when tracks are merged. a11y part of wpt/css/css-tables/column-track-merging.html</p>
+
+<table id="td_auto">
+<caption>auto</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="td_auto_width" style="width:400px">
+<caption>auto 400px</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="td_fixed" style="table-layout:fixed; width: 400px">
+<caption>fixed 400px</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="col_fixed_130" style="table-layout:fixed; width: 130px">
+<col span=10>
+<caption>col fixed 130px</caption>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+</main>
+<script>
+
+  test(function() {
+    assert_true(!!window.accessibilityController, "accessibilityController exists");
+  }, "accessibilityController exists");
+
+  // a11y tests
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_auto");
+    assert_equals(table.columnCount, 2, "has merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "td_auto table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_auto_width");
+    assert_equals(table.columnCount, 2, "has merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "td_auto_width table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_fixed");
+    assert_equals(table.columnCount, 11, "has not merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 10}", "cell.columnIndexRange");
+  }, "td_fixed table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("col_fixed_130");
+    assert_equals(table.columnCount, 10, "has not merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "col_fixed table props");
+
+ </script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/android/ChromiumWPTExpectations b/third_party/blink/web_tests/android/ChromiumWPTExpectations
index d4c7a82..f2494341 100644
--- a/third_party/blink/web_tests/android/ChromiumWPTExpectations
+++ b/third_party/blink/web_tests/android/ChromiumWPTExpectations
@@ -2861,8 +2861,6 @@
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.sharedworker.html [ Failure ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/android/WeblayerWPTExpectations b/third_party/blink/web_tests/android/WeblayerWPTExpectations
index f8d427b..c82d5a2f 100644
--- a/third_party/blink/web_tests/android/WeblayerWPTExpectations
+++ b/third_party/blink/web_tests/android/WeblayerWPTExpectations
@@ -1245,7 +1245,6 @@
 crbug.com/1050754 external/wpt/html/user-activation/propagation-crossorigin.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/html/user-activation/propagation-sameorigin.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.html [ Timeout ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.sharedworker.html [ Failure ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask.any.sharedworker.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 105ca2d..bce4643 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -3058,8 +3058,6 @@
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
 crbug.com/1050754 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.sharedworker.html [ Failure ]
 crbug.com/1050754 external/wpt/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/animations/interpolation/min-height-interpolation.html b/third_party/blink/web_tests/animations/interpolation/min-height-interpolation.html
new file mode 100644
index 0000000..608af6b
--- /dev/null
+++ b/third_party/blink/web_tests/animations/interpolation/min-height-interpolation.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  min-height: 30px;
+}
+.target {
+  width: 10px;
+  height: 10px;
+  background-color: black;
+}
+.expected {
+  background-color: green;
+}
+</style>
+<body>
+<script src="resources/interpolation-test.js"></script>
+<script>
+// Regression test for crbug.com/1175628.
+assertNoInterpolation({
+  property: 'min-height',
+  from: 'calc(25631651542881545922376008592752624554174702806001571610784437629701274% - 7px)',
+  to: 'calc(25631651542881545922376008592752624554174702806001571610784437629701274% - 7px)',
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 2ba81e37..635bf72 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -1261,6 +1261,15 @@
       ]
      }
     },
+    "styling": {
+     "font-size-number-calc-crash.svg": [
+      "6b56d9df2b426e2376f87668a98f5e21ee56e37b",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "svg-in-svg": {
      "svg-in-svg-circular-filter-reference-crash.html": [
       "51303171f09d28e3958ab74ecdce7f9cf120bd12",
@@ -244263,7 +244272,7 @@
      []
     ],
     "cors-rfc1918.idl": [
-     "083b6c3d630bb8179c1376d49bd13d48235bf6f8",
+     "d392a679ff2cca25dae7ad406d6e57fd3ac25cef",
      []
     ],
     "credential-management.idl": [
@@ -244479,7 +244488,7 @@
      []
     ],
     "js-self-profiling.idl": [
-     "2c51c1cfe8eed10c8bd92bae1fdf3bf4a9216995",
+     "a2199999eada84ff43f1a237380685c5833c4791",
      []
     ],
     "keyboard-lock.idl": [
@@ -245005,6 +245014,10 @@
     "__dir__.headers": [
      "35b10bd2ccdbfa99feb96079fafab61346d025ed",
      []
+    ],
+    "idlharness.https-expected.txt": [
+     "c1b7bf964b840d03301ce8719f251300ad1f0d76",
+     []
     ]
    },
    "keyboard-lock": {
@@ -251942,6 +251955,10 @@
      "d5d199068c5b0465f96ed643af8d7d20a889312e",
      []
     ],
+    "modify.tentative-expected.txt": [
+     "e13dca3d44115cda61741720b76a3030067fb40a",
+     []
+    ],
     "removeRange-expected.txt": [
      "51df0f5d4525db516faab89c10424d95d307804f",
      []
@@ -414382,7 +414399,7 @@
      ]
     ],
     "modify.tentative.html": [
-     "c4436fcafeb5e43ea43948183d3e64842abb8be6",
+     "37231571eddac45a9cce78cddff52b69b2af9ff6",
      [
       null,
       {}
@@ -428318,6 +428335,15 @@
      ]
     },
     "keyboard": {
+     "modifier-keys-combinations.html": [
+      "1b364ff72ce539b31ef86f4a1bcf75aed6868845",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
      "modifier-keys.html": [
       "635e5d3b7797e1fcde058f81dda1b3b2132ee375",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-invalid.html
new file mode 100644
index 0000000..c5c43a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule fallback setter with invalid values</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-fallback-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: fixed;
+  symbols: A B;
+  fallback: lower-roman;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+
+// Invalid values should be ignored
+foo_rule.fallback = 'none';
+foo_rule.fallback = 'lower-roman upper-roman'
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-ref.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-ref.html
new file mode 100644
index 0000000..da4bb59
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule fallback setter</title>
+
+<ol>
+  <div>A.</div>
+  <div>B.</div>
+  <div>iii.</div>
+</ol>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter.html
new file mode 100644
index 0000000..399463f3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-fallback-setter.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule fallback setter</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-fallback-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: fixed;
+  symbols: A B;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+foo_rule.fallback = 'lower-roman';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-invalid.html
new file mode 100644
index 0000000..e15447b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-invalid.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule negative setter with invalid values</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-negative-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: extends decimal;
+  negative: '(' ')';
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside" start="-3">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+
+// Invalid values should be ignored
+foo_rule.negative = 'X Y Z';
+foo_rule.negative = '"X" "Y" "Z"';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-ref.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-ref.html
new file mode 100644
index 0000000..7d465a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule negative setter</title>
+
+<ol>
+  <div>(3).</div>
+  <div>(2).</div>
+  <div>(1).</div>
+</ol>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter.html
new file mode 100644
index 0000000..06238841
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-negative-setter.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule negative setter</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-negative-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: extends decimal;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside" start="-3">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+foo_rule.negative = '"(" ")"';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-invalid.html
new file mode 100644
index 0000000..c263a1bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule pad setter with invalid values</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-pad-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: extends decimal;
+  pad: 3 '0';
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+
+// Invalid values should be ignored
+foo_rule.pad = '-1 "0"';
+foo_rule.pad = '3';
+foo_rule.pad = '3 "X" "Y"';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-ref.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-ref.html
new file mode 100644
index 0000000..6184686
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule pad setter</title>
+
+<ol>
+  <div>001.</div>
+  <div>002.</div>
+  <div>003.</div>
+</ol>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter.html
new file mode 100644
index 0000000..df1732d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-pad-setter.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule pad setter</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-pad-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: extends decimal;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+foo_rule.pad = '3 "0"';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-invalid.html
new file mode 100644
index 0000000..7aba3a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-invalid.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule prefix and suffix setters with invalid values</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-prefix-suffix-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: cyclic;
+  symbols: A B C;
+  prefix: '(';
+  suffix: ')';
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+
+// Invalid values should be ignored
+foo_rule.prefix = '"(" "("';
+foo_rule.prefix = ')';
+foo_rule.prefix = '123';
+
+foo_rule.suffix = '")" ")"';
+foo_rule.suffix = '(';
+foo_rule.suffix = '456';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-ref.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-ref.html
new file mode 100644
index 0000000..bf52d54a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule prefix and suffix setters</title>
+
+<ol>
+  <div>(A)</div>
+  <div>(B)</div>
+  <div>(C)</div>
+</ol>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter.html
new file mode 100644
index 0000000..899caa2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-prefix-suffix-setter.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule prefix and suffix setters</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-prefix-suffix-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: cyclic;
+  symbols: A B C;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+foo_rule.prefix = '"("';
+foo_rule.suffix = '")"';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-invalid.html
new file mode 100644
index 0000000..2fc45955
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-invalid.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule range setter with invalid values</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-range-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: cyclic;
+  symbols: A B C;
+  range: 1 2;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+
+// Invalid values should be ignored
+foo_rule.range = "1 2 3";
+foo_rule.range = "3 1"
+foo_rule.range = "1 infinity"
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-ref.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-ref.html
new file mode 100644
index 0000000..0129b46
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule range setter</title>
+
+<ol>
+  <div>A.</div>
+  <div>B.</div>
+  <div>3.</div>
+</ol>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter.html b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter.html
new file mode 100644
index 0000000..10d94f0cdb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-counter-styles/cssom/cssom-range-setter.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>CSSCounterStyleRule range setter</title>
+<link rel="help" href="https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="cssom-range-setter-ref.html">
+<style id="sheet">
+@counter-style foo {
+  system: cyclic;
+  symbols: A B C;
+}
+</style>
+
+<ol style="list-style-type: foo; list-style-position: inside">
+  <li></li>
+  <li></li>
+  <li></li>
+</ol>
+
+<script>
+// Force layout update before changing the rule
+document.body.offsetWidth;
+
+const sheet = document.getElementById('sheet');
+const foo_rule = sheet.sheet.rules[0];
+foo_rule.range = "1 2";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html b/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html
new file mode 100644
index 0000000..6dba9e6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html
@@ -0,0 +1,278 @@
+<!doctype html>
+<title>Column track merging</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="/resources/check-layout-th.js"></script>
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2" />
+<style>
+
+main table {
+  border: 10px solid gray;
+  border-spacing: 20px;
+}
+
+main td {
+  width: 50px;
+  height:50px;
+  padding: 0;
+  background:linear-gradient(to right, yellow, orange);
+}
+main caption {
+  background: #EEE;
+}
+main .desc {
+  margin-top: 20px;
+  color: rgb(50,0,0);
+}
+main pre {
+  white-space: pre-wrap;
+
+}
+</style>
+<h3>Column merging investigation</h3>
+<o>Empty columns is a column that has no originating cells</o>
+<p><a href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2">Table standard</a> discusses this under "track merging".</p>
+<ul>
+  <li>Do empty columns get coalesced?</li>
+  <li>How does this interact with table-layout:fixed, table width</li>
+  <li>Is there a difference between columns defined by COL, vs TD.colspan? Yes!</li>
+  <li>Do COLs with specified width get merged?</li>
+</ul>
+<p>Compatibility</p>
+<li>Edge17 has a bug where width of a colspanned cell always includes cell width + width of border spacing. It should be max(cell width, border spacing)</li>
+<li>Safari matches Chrome Legacy. TD-originated columns are always merged.</li>
+<li>Firefox follows the standard, but has a few bugs.</li>
+<main>
+
+<h3>TD merging</h3>
+
+<pre class="desc">Auto table, and TD.colspan=10
+  FF/Chrome Legacy/Safari: Standard. Tracks merge.
+  Edge17: Tracks do not merge. Wide cell is 180px (9 * border spacing)
+</pre>
+<table id="td_auto" data-expected-width=180>
+<caption>auto</caption>
+<tr>
+  <td colspan=10 data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(400px), and TD.colspan=10
+  FF/Chrome Legacy/Safari/Edge17: Standard. Tracks merge. Colspan cell grows because it is unconstrained.
+</pre>
+<table id="td_auto_width" style="width:400px" data-expected-width=400>
+<caption>auto 400px</caption>
+<tr>
+  <td colspan=10 data-expected-width=270></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(130px), and TD.colspan=10
+  FF/Chrome Legacy/Safari: Standard. Tracks merge. Colspan cell shrinks to min width becuase it is unconstrained.
+  Edge17: Non-compliant, buggy. Wide cell too wide, narrow cell disappears.
+</pre>
+<table id="td_auto_width_130" style="width:130px" data-expected-width=130>
+<caption>auto 130px</caption>
+<tr>
+  <td colspan=10 data-expected-width=10><div style="width:10px"></div></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(400px) table, and TD.colspan=10
+  Chrome/Safari: Non-compliant. Tracks merge. Cells are the same size, fixed algo distributes extra width evenly.
+  Firefox: Standard.
+  Edge17: Standard, buggy. Wide cell too wide. Edge's bug is that it computes max width as (width + border_spacing) instead of max(width, border_spacing).
+</pre>
+<table id="td_fixed" style="table-layout:fixed; width: 400px" data-expected-width=400>
+<caption>fixed 400px</caption>
+<tr>
+  <td colspan=10 data-expected-width=180></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(130px) table, and TD.colspan=10
+  Chrome/Safari: Non-compliant.Tracks merge, cells same size.
+  Firefox: Standard + buggy. Table does not grow.
+  Edge17: Standard + buggy. Wide cell too wide.
+</pre>
+<table id="td_fixed" style="table-layout:fixed; width: 130px" data-expected-width=310>
+<caption>fixed 130px</caption>
+<tr>
+  <td colspan=10 data-expected-width=180></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<h3>COL merging. Same tests with COL span=10 replacing TD</h3>
+
+<pre class="desc">Auto table
+  FF/Chrome Legacy/Safari, Edge17: Standard. wide cell is 50px, tracks do merge.
+</pre>
+<table id="col_auto" data-expected-width=180>
+<caption>auto</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(400px)
+  FF/Chrome Legacy/Safari, Edge17: Standard. Both cells grow the same because unconstrained.
+</pre>
+<table id="col_auto_width" style="width:400px" data-expected-width=400>
+<caption>auto 400px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=160></td>
+  <td></td>
+</tr>
+<tr>
+  <td ></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(130px)
+  FF/Chrome Legacy/Safari, Edge17: Standard. Both cells shrink.
+</pre>
+<table id="col_auto_width_130" style="width:130px" data-expected-width=130>
+<caption>auto 130px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=28><div style="width:10px"></div></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Fixed(400px) table
+  Chrome/Safari,Firefox: Standard.
+  Edge17: Buggy. Fixed cells grow to fill table.
+</pre>
+<table id="col_fixed" style="table-layout:fixed; width: 400px" data-expected-width=400>
+<caption>fixed 400px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(130px) table
+  Chrome/Safari: Standard, very buggy. Non-collapsed columns shrink to single border spacing.
+  Firefox: Standard.
+  Edge17: Non-compliant, collapses columns.
+</pre>
+<table id="col_fixed_130" style="table-layout:fixed; width: 130px" data-expected-width=340>
+<col span=10>
+<caption>fixed 130px</caption>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<h3>COL merging when COL has specified width.</h3>
+
+<ul><li>Chrome Legacy/Edge17/Safari: non-compliant, merge COLs with specified widths.
+ <li>Firefox: Standard, unless COL width is 0px. Buggy, does not include border-spacing around columns.</ul>
+<pre class="desc">Auto table, COL width 30px.
+  Chrome Legacy/Edge17/Safari: non-compliant, merge.
+  Firefox: Standard, buggy. does not include border-spacing around columns.
+</pre>
+<table id="col_auto" data-expected-width=580>
+<caption>auto col 30px</caption>
+<col span=10 style="width:30px">
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table, COL width 5%.
+  Chrome Legacy/Edge17/Safari: non-compliant, merge.
+  Firefox: Standard, buggy. does not include border-spacing around columns.
+</pre>
+<table id="col_auto" data-expected-width=640>
+<caption>auto col 10%</caption>
+<col span=5 style="width:10%">
+<tr>
+  <td data-expected-width=100></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table, COL width 0px.
+  Everyone: merges COL
+</pre>
+<table id="col_auto" data-expected-width=180>
+<caption>auto col 0px</caption>
+<col span=10 style="width:0px">
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+
+</main>
+<script>
+  checkLayout("main table");
+</script>
+
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode-expected.txt
index a6b2d0a..c5eb341 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode-expected.txt
@@ -1,32 +1,32 @@
 This is a testharness.js-based test.
 PASS Testing inserting content around link element
-FAIL Replacing text in a link with "XY" in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following Selection.collapseToEnd) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following ArrowRight key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following End key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following Selection.collapseToStart) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following ArrowLeft key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link (following Home key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link (Selection.collapse) in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link (Selection.addRange) in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link (Selection.collapse) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link (Selection.addRange) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link (Selection.collapse) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link (Selection.addRange) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to middle of a link in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to start of a link in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to end of a link in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link (Backspace) in <p><a href="about:blank">abc</a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link (execCommand("delete")) in <p><a href="about:blank">abc</a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link (Delete) in <p>[]z<a href="about:blank">abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link (execCommand("forwarddelete")) in <p>[]z<a href="about:blank">abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link (Backspace) in <p><a href="about:blank">abcd[]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link (execCommand("delete")) in <p><a href="about:blank">abcd[]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link (Delete) in <p><a href="about:blank">abc[]d</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">abc[]d</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (Backspace) in <p><a href="about:blank">z[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (execCommand("delete")) in <p><a href="about:blank">z[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (Delete) in <p><a href="about:blank">[]zabc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">[]zabc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Replacing text in a link with "XY" in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following Selection.collapseToEnd) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following ArrowRight key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following End key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following Selection.collapseToStart) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following ArrowLeft key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link (following Home key press) in <p>[abc]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link (Selection.collapse) in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link (Selection.addRange) in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link (Selection.collapse) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link (Selection.addRange) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link (Selection.collapse) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link (Selection.addRange) in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to middle of a link in <p><a href="about:blank">[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to start of a link in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to end of a link in <p><a href="about:blank">ab[]c</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link (Backspace) in <p><a href="about:blank">abc</a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link (execCommand("delete")) in <p><a href="about:blank">abc</a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link (Delete) in <p>[]z<a href="about:blank">abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link (execCommand("forwarddelete")) in <p>[]z<a href="about:blank">abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link (Backspace) in <p><a href="about:blank">abcd[]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link (execCommand("delete")) in <p><a href="about:blank">abcd[]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link (Delete) in <p><a href="about:blank">abc[]d</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">abc[]d</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (Backspace) in <p><a href="about:blank">z[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (execCommand("delete")) in <p><a href="about:blank">z[]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (Delete) in <p><a href="about:blank">[]zabc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">[]zabc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
index 6af2bc6..3506214 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
@@ -1,25 +1,25 @@
 This is a testharness.js-based test.
 PASS Testing inserting content around link element
-FAIL Inserting "XY" after setting caret position to middle of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to middle of a link containing <b> in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to start of a link containing <b> in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to end of a link containing <b> in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abc</b></a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abc</b></a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link containing <b> (Delete) in <p>[]z<a href="about:blank"><b>abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link containing <b> (execCommand("forwarddelete")) in <p>[]z<a href="about:blank"><b>abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abcd[]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abcd[]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link containing <b> (Delete) in <p><a href="about:blank"><b>abc[]d</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>abc[]d</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>z[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>z[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (Delete) in <p><a href="about:blank"><b>[]zabc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>[]zabc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link containing <b> (Selection.collapse) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link containing <b> (Selection.addRange) in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to middle of a link containing <b> in <p><a href="about:blank"><b>[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to start of a link containing <b> in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to end of a link containing <b> in <p><a href="about:blank"><b>ab[]c</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abc</b></a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abc</b></a>d[]</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link containing <b> (Delete) in <p>[]z<a href="about:blank"><b>abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link containing <b> (execCommand("forwarddelete")) in <p>[]z<a href="about:blank"><b>abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abcd[]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abcd[]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link containing <b> (Delete) in <p><a href="about:blank"><b>abc[]d</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>abc[]d</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>z[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>z[]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (Delete) in <p><a href="about:blank"><b>[]zabc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>[]zabc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
index f51ea12..4075ee2 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
@@ -1,32 +1,32 @@
 This is a testharness.js-based test.
 PASS Testing inserting content around link element
-FAIL Replacing text in a link in <b> with "XY" in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following Selection.collapseToEnd) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following ArrowRight key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following End key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following Selection.collapseToStart) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following ArrowLeft key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after making a link in <b> (following Home key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to middle of a link in <b> in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to start of a link in <b> in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to end of a link in <b> in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link in <b> (Backspace) in <p><b><a href="about:blank">abc</a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abc</a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link in <b> (Delete) in <p><b>[]z<a href="about:blank">abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link in <b> (execCommand("forwarddelete")) in <p><b>[]z<a href="about:blank">abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> (Backspace) in <p><b><a href="about:blank">abcd[]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abcd[]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> (Delete) in <p><b><a href="about:blank">abc[]d</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">abc[]d</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (Backspace) in <p><b><a href="about:blank">z[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">z[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (Delete) in <p><b><a href="about:blank">[]zabc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">[]zabc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Replacing text in a link in <b> with "XY" in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following Selection.collapseToEnd) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following ArrowRight key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following End key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following Selection.collapseToStart) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following ArrowLeft key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after making a link in <b> (following Home key press) in <p><b>[abc]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link in <b> (Selection.collapse) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link in <b> (Selection.addRange) in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to middle of a link in <b> in <p><b><a href="about:blank">[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to start of a link in <b> in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to end of a link in <b> in <p><b><a href="about:blank">ab[]c</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link in <b> (Backspace) in <p><b><a href="about:blank">abc</a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abc</a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link in <b> (Delete) in <p><b>[]z<a href="about:blank">abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link in <b> (execCommand("forwarddelete")) in <p><b>[]z<a href="about:blank">abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> (Backspace) in <p><b><a href="about:blank">abcd[]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abcd[]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> (Delete) in <p><b><a href="about:blank">abc[]d</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">abc[]d</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (Backspace) in <p><b><a href="about:blank">z[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">z[]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (Delete) in <p><b><a href="about:blank">[]zabc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">[]zabc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
index e8d3431..f717a62 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
@@ -1,25 +1,25 @@
 This is a testharness.js-based test.
 PASS Testing inserting content around link element
-FAIL Inserting "XY" after setting caret position to middle of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to middle of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to start of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after setting caret position to end of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to middle of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to start of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after modifying caret position to end of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abc</i></a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting following character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abc</i></a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link in <b> and containing <i> (Delete) in <p><b>[]z<a href="about:blank"><i>abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting a previous character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b>[]z<a href="about:blank"><i>abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abcd[]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abcd[]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>abc[]d</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>abc[]d</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>z[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>z[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>[]zabc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>[]zabc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to middle of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to start of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link in <b> and containing <i> (Selection.collapse) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after setting caret position to end of a link in <b> and containing <i> (Selection.addRange) in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to middle of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to start of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after modifying caret position to end of a link in <b> and containing <i> in <p><b><a href="about:blank"><i>ab[]c</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abc</i></a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting following character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abc</i></a>d[]</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link in <b> and containing <i> (Delete) in <p><b>[]z<a href="about:blank"><i>abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting a previous character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b>[]z<a href="about:blank"><i>abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abcd[]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abcd[]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>abc[]d</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>abc[]d</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>z[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>z[]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>[]zabc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>[]zabc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode-expected.txt
index fb4c861..2774722 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode-expected.txt
@@ -1,34 +1,34 @@
 This is a testharness.js-based test.
 PASS Testing inserting content at non-collapsed selection around link element
-FAIL Inserting "XY" after deleting first character of a link (Direct typing) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Direct typing) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link (Direct typing) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link (Direct typing) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 same links (Direct typing) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 different links (Direct typing) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (Backspace) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Backspace) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link (Backspace) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link (Backspace) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 same links (Backspace) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 different links (Backspace) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (Delete) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Delete) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link (Delete) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link (Delete) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 same links (Delete) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 different links (Delete) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (execCommand("delete")) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (execCommand("delete")) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link (execCommand("delete")) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link (execCommand("delete")) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 same links (execCommand("delete")) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 different links (execCommand("delete")) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (execCommand("forwarddelete")) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link (execCommand("forwarddelete")) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link (execCommand("forwarddelete")) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 same links (execCommand("forwarddelete")) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text between 2 different links (execCommand("forwarddelete")) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (Direct typing) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Direct typing) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link (Direct typing) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link (Direct typing) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 same links (Direct typing) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 different links (Direct typing) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (Backspace) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Backspace) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link (Backspace) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link (Backspace) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 same links (Backspace) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 different links (Backspace) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (Delete) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (Delete) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link (Delete) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link (Delete) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 same links (Delete) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 different links (Delete) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (execCommand("delete")) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (execCommand("delete")) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link (execCommand("delete")) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link (execCommand("delete")) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 same links (execCommand("delete")) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 different links (execCommand("delete")) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link (execCommand("forwarddelete")) in <p><a href="about:blank">[z]abc</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link (execCommand("forwarddelete")) in <p><a href="about:blank">abc[d]</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link (execCommand("forwarddelete")) in <p><a href="about:blank">ab[cd</a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link (execCommand("forwarddelete")) in <p>a[bc<a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 same links (execCommand("forwarddelete")) in <p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text between 2 different links (execCommand("forwarddelete")) in <p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
index 2584e79..7a6fc54 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt
@@ -1,24 +1,24 @@
 This is a testharness.js-based test.
 PASS Testing inserting content at non-collapsed selection around link element
-FAIL Inserting "XY" after deleting first character of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Direct typing) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Backspace) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Backspace) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (Delete) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Delete) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Delete) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Delete) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link containing <b> (execCommand("delete")) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link containing <b> (execCommand("forwarddelete")) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Direct typing) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Direct typing) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (Backspace) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Backspace) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Backspace) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Backspace) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (Delete) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (Delete) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link containing <b> (Delete) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link containing <b> (Delete) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link containing <b> (execCommand("delete")) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link containing <b> (execCommand("delete")) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>[z]abc</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>abc[d]</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link containing <b> (execCommand("forwarddelete")) in <p><a href="about:blank"><b>ab[cd</b></a>de]f</p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link containing <b> (execCommand("forwarddelete")) in <p>a[bc<a href="about:blank"><b>de]f</b></a></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
index 739ba50..9b425c4 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt
@@ -1,24 +1,24 @@
 This is a testharness.js-based test.
 PASS Testing inserting content at non-collapsed selection around link element
-FAIL Inserting "XY" after deleting first character of a link in <b> (Direct typing) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Direct typing) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> (Direct typing) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> (Direct typing) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (Backspace) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Backspace) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> (Backspace) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> (Backspace) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (Delete) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Delete) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> (Delete) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> (Delete) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> (execCommand("delete")) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> (execCommand("forwarddelete")) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (Direct typing) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Direct typing) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> (Direct typing) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> (Direct typing) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (Backspace) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Backspace) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> (Backspace) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> (Backspace) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (Delete) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (Delete) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> (Delete) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> (Delete) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> (execCommand("delete")) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> (execCommand("delete")) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">[z]abc</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">abc[d]</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> (execCommand("forwarddelete")) in <p><b><a href="about:blank">ab[cd</a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> (execCommand("forwarddelete")) in <p><b>a[bc<a href="about:blank">de]f</a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
index 9d0b49b..2cd75e3 100644
--- a/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/editing/other/typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt
@@ -1,24 +1,24 @@
 This is a testharness.js-based test.
 PASS Testing inserting content at non-collapsed selection around link element
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Direct typing) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Backspace) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Delete) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (execCommand("delete")) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
-FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send keys in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Direct typing) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Direct typing) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Backspace) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Backspace) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (Delete) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (Delete) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (execCommand("delete")) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (execCommand("delete")) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting first character of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>[z]abc</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting last character in a non-collapsed range of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>abc[d]</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text after middle of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b><a href="about:blank"><i>ab[cd</i></a>de]f</b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
+FAIL Inserting "XY" after deleting text before middle of a link in <b> and containing <i> (execCommand("forwarddelete")) in <p><b>a[bc<a href="about:blank"><i>de]f</i></a></b></p> promise_test: Unhandled rejection with value: object "Error: can only send actions in top-level window"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.perspective.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.perspective.html
new file mode 100644
index 0000000..6aa4a224
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.perspective.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.perspective</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.perspective</h1>
+<p class="desc">perspective() results in the correct transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("perspective() results in the correct transformation matrix");
+_addTest(function(canvas, ctx) {
+
+const length = 100;
+ctx.perspective(length);
+const domMatrix = new DOMMatrix();
+domMatrix.m34 = -1/length;
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
+ctx.rotateAxis(1, 2, 3, 4);
+domMatrix.rotateAxisAngleSelf(1, 2, 3, rad2deg(4));
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
+
+
+});
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.X.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.X.html
index c55a9bf..42a4e3c 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.X.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.X.html
@@ -24,12 +24,10 @@
 const domMatrix = new DOMMatrix();
 ctx.rotate3d(angle, 0, 0);
 domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
-let canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 ctx.rotate3d(angle, 0, 0);
 domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
-canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Y.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Y.html
index 12bf507..5006769 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Y.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Y.html
@@ -24,12 +24,10 @@
 const domMatrix = new DOMMatrix();
 ctx.rotate3d(0, angle, 0);
 domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
-let canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 ctx.rotate3d(0, angle, 0);
 domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
-canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Z.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Z.html
index 8a26466..71e113d 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Z.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.Z.html
@@ -24,12 +24,10 @@
 const domMatrix = new DOMMatrix();
 ctx.rotate3d(0, 0, angle);
 domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
-let canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 ctx.rotate3d(0, 0, angle);
 domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
-canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix)
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.html
index 80812bd..104e0870 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotate3d.html
@@ -26,12 +26,10 @@
 const domMatrix = new DOMMatrix();
 ctx.rotate3d(angleX, angleY, angleZ);
 domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
-let canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix);
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
 ctx.rotate3d(angleX, angleY, angleZ);
 domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
-canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix);
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
 
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotateAxis.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotateAxis.html
index 47258df..be0785a 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotateAxis.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/transformations/2d.transformation.rotateAxis.html
@@ -25,12 +25,10 @@
 const domMatrix = new DOMMatrix();
 ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
 domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
-let canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix);
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
 ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
 domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
-canvasMatrix = ctx.getTransform();
-_assertMatricesApproxEqual(domMatrix, canvasMatrix);
+_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
 
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/transformations.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/transformations.yaml
index aa56c85..65346ca 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/transformations.yaml
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/transformations.yaml
@@ -473,12 +473,10 @@
     const domMatrix = new DOMMatrix();
     ctx.rotate3d(angle, 0, 0);
     domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
-    let canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
     ctx.rotate3d(angle, 0, 0);
     domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
-    canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 - name: 2d.transformation.rotate3d.Y
   desc: rotate3d() around the y axis results in the correct transformation matrix
@@ -490,12 +488,10 @@
     const domMatrix = new DOMMatrix();
     ctx.rotate3d(0, angle, 0);
     domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
-    let canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
     ctx.rotate3d(0, angle, 0);
     domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
-    canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 - name: 2d.transformation.rotate3d.Z
   desc: rotate3d() around the z axis results in the correct transformation matrix
@@ -507,12 +503,10 @@
     const domMatrix = new DOMMatrix();
     ctx.rotate3d(0, 0, angle);
     domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
-    let canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
     ctx.rotate3d(0, 0, angle);
     domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
-    canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix)
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform())
 
 - name: 2d.transformation.rotate3d
   desc: rotate3d() results in the correct transformation matrix
@@ -526,12 +520,10 @@
     const domMatrix = new DOMMatrix();
     ctx.rotate3d(angleX, angleY, angleZ);
     domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
-    let canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix);
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
     ctx.rotate3d(angleX, angleY, angleZ);
     domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
-    canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix);
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
 
 - name: 2d.transformation.rotateAxis
   desc: rotateAxis() results in the correct transformation matrix
@@ -544,9 +536,21 @@
     const domMatrix = new DOMMatrix();
     ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
     domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
-    let canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix);
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
     ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
     domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
-    canvasMatrix = ctx.getTransform();
-    _assertMatricesApproxEqual(domMatrix, canvasMatrix);
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
+
+- name: 2d.transformation.perspective
+  desc: perspective() results in the correct transformation matrix
+  testing:
+  - 2d.transformation.perspective
+  code: |
+    const length = 100;
+    ctx.perspective(length);
+    const domMatrix = new DOMMatrix();
+    domMatrix.m34 = -1/length;
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
+    ctx.rotateAxis(1, 2, 3, 4);
+    domMatrix.rotateAxisAngleSelf(1, 2, 3, rad2deg(4));
+    _assertMatricesApproxEqual(domMatrix, ctx.getTransform());
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
new file mode 100644
index 0000000..1781afb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popup light dismiss behavior</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popup-utils.js"></script>
+
+<body>
+
+
+<button id=b1 onclick='p1.show()'>Popup 1</button>
+<span id=outside>Outside all popups</span>
+<popup id=p1 anchor=b1>
+  <span id=inside1>Inside popup 1</span>
+  <button id=b2 onclick='p2.show()'>Popup 2</button>
+</popup>
+<popup id=p2 anchor=b2>
+  <span id=inside2>Inside popup 2</span>
+</popup>
+
+<style>
+  #p1 { top:50px; }
+  #p2 { top:50px; left:250px; }
+  popup { border: 5px solid red; }
+</style>
+
+
+<script>
+  function clickOn(element) {
+    const actions = new test_driver.Actions();
+    actions.pointerMove(0, 0, {origin: element});
+    actions.pointerDown({button: actions.ButtonType.LEFT});
+    actions.pointerUp({button: actions.ButtonType.LEFT});
+    return actions.send();
+  }
+
+  function pressKey(key) {
+    const actions = new test_driver.Actions();
+    actions.keyDown(key);
+    actions.keyUp(key);
+    return actions.send();
+  }
+
+  (async function() {
+    setup({ explicit_done: true });
+
+    const popup1 = document.querySelector('#p1');
+    const popup2 = document.querySelector('#p2');
+    const outside = document.querySelector('#outside');
+    const inside1 = document.querySelector('#inside1');
+    const inside2 = document.querySelector('#inside2');
+
+    assert_false(popup1.open);
+    popup1.show();
+    assert_true(popup1.open);
+    await clickOn(outside);
+    test(t => {
+      assert_false(popup1.open);
+    },'Clicking outside a popup will dismiss the popup');
+
+    assert_false(popup1.open);
+    popup1.show();
+    await clickOn(inside1);
+    test(t => {
+      assert_true(popup1.open);
+      popup1.hide();
+    },'Clicking inside a popup does not close that popup');
+
+
+    popup1.show();
+    popup2.show();
+    await clickOn(inside2);
+    test(t => {
+      assert_true(popup1.open);
+      assert_true(popup2.open);
+      popup1.hide();
+    },'Clicking inside a child popup shouldn\'t close either popup');
+
+    assert_false(popup1.open);
+    assert_false(popup2.open);
+    popup1.show();
+    popup2.show();
+    await clickOn(inside1);
+    test(t => {
+      assert_true(popup1.open);
+      assert_false(popup2.open);
+      popup1.hide();
+    },'Clicking inside a parent popup should close child popup');
+
+    done();
+  })();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html
index b977f0c2..3e0795b14 100644
--- a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html
@@ -36,7 +36,7 @@
 <script>
 let keys = [];
 
-async_test(t => {
+promise_test(async t => {
   let test1 = document.getElementById("test1");
   let test2 = document.getElementById("test2");
   document.getElementById("test1").addEventListener("click",
@@ -60,8 +60,7 @@
     .pointerDown()
     .pointerUp();
 
-  actions.send()
-    .then(t.step_func_done(() => assert_array_equals(keys, [true, true, false])))
-    .catch(e => t.step_func(() => assert_unreached("Actions sequence failed " + e)));
+  await actions.send();
+  assert_array_equals(keys, [true, true, false]);
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/cors-rfc1918.idl b/third_party/blink/web_tests/external/wpt/interfaces/cors-rfc1918.idl
index 083b6c3d6..d392a679 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/cors-rfc1918.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/cors-rfc1918.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
 // Content was automatically extracted by Reffy into webref
 // (https://github.com/w3c/webref)
-// Source: CORS and RFC1918 (https://wicg.github.io/cors-rfc1918/)
+// Source: CORS and RFC1918 (https://wicg.github.io/private-network-access/)
 
 enum AddressSpace { "local", "private", "public" };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/js-self-profiling.idl b/third_party/blink/web_tests/external/wpt/interfaces/js-self-profiling.idl
index 2c51c1c..a219999 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/js-self-profiling.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/js-self-profiling.idl
@@ -4,7 +4,7 @@
 // Source: JS Self-Profiling API (https://wicg.github.io/js-self-profiling/)
 
 [Exposed=(Window,Worker)]
-interface Profiler {
+interface Profiler : EventTarget {
   readonly attribute DOMHighResTimeStamp sampleInterval;
   readonly attribute boolean stopped;
 
diff --git a/third_party/blink/web_tests/external/wpt/js-self-profiling/idlharness.https-expected.txt b/third_party/blink/web_tests/external/wpt/js-self-profiling/idlharness.https-expected.txt
new file mode 100644
index 0000000..c1b7bf9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/js-self-profiling/idlharness.https-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial interface Performance: original interface defined
+PASS Partial interface Performance: valid exposure set
+PASS Partial interface Performance: member names are unique
+FAIL Profiler interface: existence and properties of interface object assert_equals: prototype of Profiler is not EventTarget expected function "function EventTarget() { [native code] }" but got function "function () { [native code] }"
+PASS Profiler interface object length
+PASS Profiler interface object name
+FAIL Profiler interface: existence and properties of interface prototype object assert_equals: prototype of Profiler.prototype is not EventTarget.prototype expected object "[object EventTarget]" but got object "[object Object]"
+PASS Profiler interface: existence and properties of interface prototype object's "constructor" property
+PASS Profiler interface: existence and properties of interface prototype object's @@unscopables property
+PASS Profiler interface: attribute sampleInterval
+PASS Profiler interface: attribute stopped
+PASS Profiler interface: operation stop()
+PASS Profiler must be primary interface of profiler
+PASS Stringification of profiler
+PASS Profiler interface: profiler must inherit property "sampleInterval" with the proper type
+PASS Profiler interface: profiler must inherit property "stopped" with the proper type
+PASS Profiler interface: profiler must inherit property "stop()" with the proper type
+PASS Performance interface: operation profile(ProfilerInitOptions)
+PASS Performance interface: performance must inherit property "profile(ProfilerInitOptions)" with the proper type
+PASS Performance interface: calling profile(ProfilerInitOptions) on performance with too few arguments must throw TypeError
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt b/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt
index 3be14d3..3d8bfb8 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt
@@ -1,4 +1,5 @@
 This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: we do not support keydown and keyup actions, please use test_driver.send_keys
 FAIL Tests that the mouse events with some keys pressed. assert_true: Timed out waiting for pointermove expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window-expected.txt
deleted file mode 100644
index 22bb34a..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window-expected.txt
+++ /dev/null
@@ -1,145 +0,0 @@
-This is a testharness.js-based test.
-Found 141 tests; 135 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface Element: original interface defined
-PASS Partial interface Element: member names are unique
-PASS Partial interface mixin GlobalEventHandlers: original interface mixin defined
-PASS Partial interface mixin GlobalEventHandlers: member names are unique
-PASS Partial interface Navigator: original interface defined
-PASS Partial interface Navigator: member names are unique
-PASS Partial interface UIEvent: member names are unique
-PASS Partial interface Document: member names are unique
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Partial interface Document[2]: member names are unique
-PASS Partial interface Window: member names are unique
-PASS Document includes GlobalEventHandlers: member names are unique
-PASS Document includes DocumentAndElementEventHandlers: member names are unique
-PASS Document includes NonElementParentNode: member names are unique
-PASS Document includes ParentNode: member names are unique
-PASS Document includes XPathEvaluatorBase: member names are unique
-PASS HTMLElement includes GlobalEventHandlers: member names are unique
-PASS HTMLElement includes DocumentAndElementEventHandlers: member names are unique
-PASS HTMLElement includes ElementContentEditable: member names are unique
-PASS HTMLElement includes HTMLOrSVGElement: member names are unique
-PASS Window includes GlobalEventHandlers: member names are unique
-PASS Window includes WindowEventHandlers: member names are unique
-PASS Window includes WindowOrWorkerGlobalScope: member names are unique
-PASS Window includes AnimationFrameProvider: member names are unique
-PASS Window includes WindowSessionStorage: member names are unique
-PASS Window includes WindowLocalStorage: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS Element includes ParentNode: member names are unique
-PASS Element includes NonDocumentTypeChildNode: member names are unique
-PASS Element includes ChildNode: member names are unique
-PASS Element includes Slottable: member names are unique
-PASS PointerEvent interface: existence and properties of interface object
-PASS PointerEvent interface object length
-PASS PointerEvent interface object name
-PASS PointerEvent interface: existence and properties of interface prototype object
-PASS PointerEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS PointerEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS PointerEvent interface: attribute pointerId
-PASS PointerEvent interface: attribute width
-PASS PointerEvent interface: attribute height
-PASS PointerEvent interface: attribute pressure
-PASS PointerEvent interface: attribute tangentialPressure
-PASS PointerEvent interface: attribute tiltX
-PASS PointerEvent interface: attribute tiltY
-PASS PointerEvent interface: attribute twist
-PASS PointerEvent interface: attribute altitudeAngle
-PASS PointerEvent interface: attribute azimuthAngle
-PASS PointerEvent interface: attribute pointerType
-PASS PointerEvent interface: attribute isPrimary
-PASS PointerEvent interface: operation getCoalescedEvents()
-PASS PointerEvent interface: operation getPredictedEvents()
-PASS PointerEvent must be primary interface of new PointerEvent("type")
-PASS Stringification of new PointerEvent("type")
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "pointerId" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "width" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "height" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "pressure" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "tangentialPressure" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "tiltX" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "tiltY" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "twist" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "altitudeAngle" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "azimuthAngle" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "pointerType" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "isPrimary" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "getCoalescedEvents()" with the proper type
-PASS PointerEvent interface: new PointerEvent("type") must inherit property "getPredictedEvents()" with the proper type
-PASS HTMLElement interface: attribute ongotpointercapture
-PASS HTMLElement interface: attribute onlostpointercapture
-PASS HTMLElement interface: attribute onpointerdown
-PASS HTMLElement interface: attribute onpointermove
-PASS HTMLElement interface: attribute onpointerrawupdate
-PASS HTMLElement interface: attribute onpointerup
-PASS HTMLElement interface: attribute onpointercancel
-PASS HTMLElement interface: attribute onpointerover
-PASS HTMLElement interface: attribute onpointerout
-PASS HTMLElement interface: attribute onpointerenter
-PASS HTMLElement interface: attribute onpointerleave
-PASS Window interface: attribute ongotpointercapture
-PASS Window interface: attribute onlostpointercapture
-PASS Window interface: attribute onpointerdown
-PASS Window interface: attribute onpointermove
-PASS Window interface: attribute onpointerrawupdate
-PASS Window interface: attribute onpointerup
-PASS Window interface: attribute onpointercancel
-PASS Window interface: attribute onpointerover
-PASS Window interface: attribute onpointerout
-PASS Window interface: attribute onpointerenter
-PASS Window interface: attribute onpointerleave
-PASS Window interface: window must inherit property "ongotpointercapture" with the proper type
-PASS Window interface: window must inherit property "onlostpointercapture" with the proper type
-PASS Window interface: window must inherit property "onpointerdown" with the proper type
-PASS Window interface: window must inherit property "onpointermove" with the proper type
-PASS Window interface: window must inherit property "onpointerrawupdate" with the proper type
-PASS Window interface: window must inherit property "onpointerup" with the proper type
-PASS Window interface: window must inherit property "onpointercancel" with the proper type
-PASS Window interface: window must inherit property "onpointerover" with the proper type
-PASS Window interface: window must inherit property "onpointerout" with the proper type
-PASS Window interface: window must inherit property "onpointerenter" with the proper type
-PASS Window interface: window must inherit property "onpointerleave" with the proper type
-PASS Navigator interface: attribute maxTouchPoints
-PASS Navigator interface: navigator must inherit property "maxTouchPoints" with the proper type
-PASS Document interface: attribute ongotpointercapture
-PASS Document interface: attribute onlostpointercapture
-PASS Document interface: attribute onpointerdown
-PASS Document interface: attribute onpointermove
-PASS Document interface: attribute onpointerrawupdate
-PASS Document interface: attribute onpointerup
-PASS Document interface: attribute onpointercancel
-PASS Document interface: attribute onpointerover
-PASS Document interface: attribute onpointerout
-PASS Document interface: attribute onpointerenter
-PASS Document interface: attribute onpointerleave
-PASS Document interface: document must inherit property "ongotpointercapture" with the proper type
-PASS Document interface: document must inherit property "onlostpointercapture" with the proper type
-PASS Document interface: document must inherit property "onpointerdown" with the proper type
-PASS Document interface: document must inherit property "onpointermove" with the proper type
-PASS Document interface: document must inherit property "onpointerrawupdate" with the proper type
-PASS Document interface: document must inherit property "onpointerup" with the proper type
-PASS Document interface: document must inherit property "onpointercancel" with the proper type
-PASS Document interface: document must inherit property "onpointerover" with the proper type
-PASS Document interface: document must inherit property "onpointerout" with the proper type
-PASS Document interface: document must inherit property "onpointerenter" with the proper type
-PASS Document interface: document must inherit property "onpointerleave" with the proper type
-PASS Element interface: operation setPointerCapture(long)
-PASS Element interface: operation releasePointerCapture(long)
-PASS Element interface: operation hasPointerCapture(long)
-FAIL Element interface: document must inherit property "setPointerCapture(long)" with the proper type assert_inherits: property "setPointerCapture" not found in prototype chain
-FAIL Element interface: calling setPointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "setPointerCapture" not found in prototype chain
-FAIL Element interface: document must inherit property "releasePointerCapture(long)" with the proper type assert_inherits: property "releasePointerCapture" not found in prototype chain
-FAIL Element interface: calling releasePointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "releasePointerCapture" not found in prototype chain
-FAIL Element interface: document must inherit property "hasPointerCapture(long)" with the proper type assert_inherits: property "hasPointerCapture" not found in prototype chain
-FAIL Element interface: calling hasPointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "hasPointerCapture" not found in prototype chain
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js b/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js
index b41a65f3..e6e84fa 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js
@@ -12,7 +12,7 @@
   idl_array => {
     idl_array.add_objects({
       Document: ['document'],
-      Element: ['document'],
+      Element: ['document.body'],
       Window: ['window'],
       Navigator: ['navigator'],
       PointerEvent: ['new PointerEvent("type")']
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js.ini b/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js.ini
deleted file mode 100644
index 2c8f437..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/idlharness.window.js.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[idlharness.window.html]
-  [Element interface: document must inherit property "setPointerCapture(long)" with the proper type]
-    expected: FAIL
-
-  [Element interface: calling setPointerCapture(long) on document with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Element interface: document must inherit property "releasePointerCapture(long)" with the proper type]
-    expected: FAIL
-
-  [Element interface: calling releasePointerCapture(long) on document with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Element interface: document must inherit property "hasPointerCapture(long)" with the proper type]
-    expected: FAIL
-
-  [Element interface: calling hasPointerCapture(long) on document with too few arguments must throw TypeError]
-    expected: FAIL
-
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
index e3b21fb..fe9dc3f9 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
@@ -613,6 +613,68 @@
     this.hit_test_source_creation_callback_ = callback;
   }
 
+  setLightEstimate(fakeXrLightEstimateInit) {
+    if (!fakeXrLightEstimateInit.sphericalHarmonicsCoefficients) {
+      throw new TypeError("sphericalHarmonicsCoefficients must be set");
+    }
+
+    if (fakeXrLightEstimateInit.sphericalHarmonicsCoefficients.length != 27) {
+      throw new TypeError("Must supply all 27 sphericalHarmonicsCoefficients");
+    }
+
+    if (fakeXrLightEstimateInit.primaryLightDirection && fakeXrLightEstimateInit.primaryLightDirection.w != 0) {
+      throw new TypeError("W component of primaryLightDirection must be 0");
+    }
+
+    if (fakeXrLightEstimateInit.primaryLightIntensity && fakeXrLightEstimateInit.primaryLightIntensity.w != 1) {
+      throw new TypeError("W component of primaryLightIntensity must be 1");
+    }
+
+    // If the primaryLightDirection or primaryLightIntensity aren't set, we need to set them
+    // to the defaults that the spec expects. ArCore will either give us everything or nothing,
+    // so these aren't nullable on the mojom.
+    if (!fakeXrLightEstimateInit.primaryLightDirection) {
+      fakeXrLightEstimateInit.primaryLightDirection = { x: 0.0, y: 1.0, z: 0.0, w: 0.0 };
+    }
+
+    if (!fakeXrLightEstimateInit.primaryLightIntensity) {
+      fakeXrLightEstimateInit.primaryLightIntensity = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 };
+    }
+
+    let c = fakeXrLightEstimateInit.sphericalHarmonicsCoefficients;
+
+    this.light_estimate_ = {
+      lightProbe: {
+        // XRSphereicalHarmonics
+        sphericalHarmonics: {
+          coefficients: [
+            { red: c[0],  green: c[1],  blue: c[2] },
+            { red: c[3],  green: c[4],  blue: c[5] },
+            { red: c[6],  green: c[7],  blue: c[8] },
+            { red: c[9],  green: c[10], blue: c[11] },
+            { red: c[12], green: c[13], blue: c[14] },
+            { red: c[15], green: c[16], blue: c[17] },
+            { red: c[18], green: c[19], blue: c[20] },
+            { red: c[21], green: c[22], blue: c[23] },
+            { red: c[24], green: c[25], blue: c[26] }
+          ]
+        },
+        // Vector3dF
+        mainLightDirection: {
+          x: fakeXrLightEstimateInit.primaryLightDirection.x,
+          y: fakeXrLightEstimateInit.primaryLightDirection.y,
+          z: fakeXrLightEstimateInit.primaryLightDirection.z
+        },
+        // RgbTupleF32
+        mainLightIntensity: {
+          red:   fakeXrLightEstimateInit.primaryLightIntensity.x,
+          green: fakeXrLightEstimateInit.primaryLightIntensity.y,
+          blue:  fakeXrLightEstimateInit.primaryLightIntensity.z
+        }
+      }
+    }
+  }
+
   // Helper methods
   getNonImmersiveDisplayInfo() {
     const displayInfo = this.getImmersiveDisplayInfo();
@@ -786,6 +848,7 @@
           renderingTimeRatio: 0,
           stageParameters: this.stageParameters_,
           stageParametersId: this.stageParametersId_,
+          lightEstimationData: this.light_estimate_
         };
 
         this.next_frame_id_++;
diff --git a/third_party/blink/web_tests/external/wpt/selection/modify.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/selection/modify.tentative-expected.txt
new file mode 100644
index 0000000..e13dca3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/modify.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+PASS Stop at previous word boundary when whitespaces are trimmed
+PASS Jump linefeed forward
+PASS Jump linefeed backward
+PASS Jump <br> forward
+PASS Jump <br> backward
+PASS Jump <br> forward which follows a linefeed
+FAIL Jump <br> backward which follows a linefeed assert_equals: expected Text node "foo
+" but got Element node <pre id="preLinefeedBr">foo
+<br>
+bar
+</pre>
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/selection/modify.tentative.html b/third_party/blink/web_tests/external/wpt/selection/modify.tentative.html
index c4436fca..37231571 100644
--- a/third_party/blink/web_tests/external/wpt/selection/modify.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/selection/modify.tentative.html
@@ -6,6 +6,21 @@
 
 <div>Test, these are <strong id="strong"> strong </strong> <em id="em"> italic </em> normal.</div>
 
+<pre id="preLinefeed">
+foo
+bar
+</pre>
+
+<pre id="preBr">
+foo<br>bar
+</pre>
+
+<pre id="preLinefeedBr">
+foo
+<br>
+bar
+</pre>
+
 <script>
 const selection = getSelection();
 test(() => {
@@ -18,4 +33,58 @@
   assert_equals(selection.focusNode, em.childNodes[0]);
   assert_equals(selection.focusOffset, 1);
 }, "Stop at previous word boundary when whitespaces are trimmed");
+
+test(() => {
+  const preLinefeed = document.getElementById("preLinefeed");
+  const textChild = preLinefeed.childNodes[0];
+  selection.collapse(textChild, 3);
+  selection.modify("move", "forward", "character");
+  assert_equals(selection.focusNode, textChild);
+  assert_equals(selection.focusOffset, 4);
+}, "Jump linefeed forward");
+
+test(() => {
+  const preLinefeed = document.getElementById("preLinefeed");
+  const textChild = preLinefeed.childNodes[0];
+  selection.collapse(textChild, 4);
+  selection.modify("move", "backward", "character");
+  assert_equals(selection.focusNode, textChild);
+  assert_equals(selection.focusOffset, 3);
+}, "Jump linefeed backward");
+
+test(() => {
+  const preBr = document.getElementById("preBr");
+  const [firstTextChild, br, secondTextChild] = preBr.childNodes;
+  selection.collapse(firstTextChild, 3);
+  selection.modify("move", "forward", "character");
+  assert_equals(selection.focusNode, secondTextChild);
+  assert_equals(selection.focusOffset, 0);
+}, "Jump <br> forward");
+
+test(() => {
+  const preBr = document.getElementById("preBr");
+  const [firstTextChild, br, secondTextChild] = preBr.childNodes;
+  selection.collapse(secondTextChild, 0);
+  selection.modify("move", "backward", "character");
+  assert_equals(selection.focusNode, firstTextChild);
+  assert_equals(selection.focusOffset, 3);
+}, "Jump <br> backward");
+
+test(() => {
+  const preLinefeedBr = document.getElementById("preLinefeedBr");
+  selection.collapse(preLinefeedBr, 1);
+  selection.modify("move", "forward", "character");
+  const secondTextChild = preLinefeedBr.childNodes[2];
+  assert_equals(selection.focusNode, secondTextChild);
+  assert_equals(selection.focusOffset, 0);
+}, "Jump <br> forward which follows a linefeed");
+
+test(() => {
+  const preLinefeedBr = document.getElementById("preLinefeedBr");
+  selection.collapse(preLinefeedBr, 2);
+  selection.modify("move", "backward", "character");
+  const textChild = preLinefeedBr.childNodes[0];
+  assert_equals(selection.focusNode, textChild);
+  assert_equals(selection.focusOffset, textChild.textContent.length);
+}, "Jump <br> backward which follows a linefeed");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/repeatcount-numeric-limit.tentative.svg b/third_party/blink/web_tests/external/wpt/svg/animations/repeatcount-numeric-limit.tentative.svg
new file mode 100644
index 0000000..aa043255
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/repeatcount-numeric-limit.tentative.svg
@@ -0,0 +1,26 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:h="http://www.w3.org/1999/xhtml">
+  <title>A huge 'repeatCount' (1e+309) is treated as unspecified</title>
+  <h:link rel="help" href="https://svgwg.org/specs/animations/#TimingAttributes"/>
+  <h:script src="/resources/testharness.js"/>
+  <h:script src="/resources/testharnessreport.js"/>
+
+  <rect width="50" height="100" fill="blue">
+    <animate attributeName="fill" from="#007f00" to="green" dur="10ms" fill="freeze"
+             repeatCount="1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
+  </rect>
+  <rect width="50" height="100" fill="blue" x="50">
+    <animate attributeName="fill" from="#007f00" to="green" dur="10ms" fill="freeze"
+             repeatCount="1e+309"/>
+  </rect>
+  <script>
+    promise_test(t => {
+      let watchers = Array.from(document.getElementsByTagName('animate')).map(element => {
+        let watcher = new EventWatcher(t, element, ['endEvent', 'repeatEvent']);
+        return watcher.wait_for('endEvent').then(() => {
+          assert_equals(getComputedStyle(element).fill, 'rgb(0, 128, 0)');
+        });
+      });
+      return Promise.all(watchers);
+    });
+  </script>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/uievents/keyboard/modifier-keys-combinations.html b/third_party/blink/web_tests/external/wpt/uievents/keyboard/modifier-keys-combinations.html
new file mode 100644
index 0000000..1b364ff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/uievents/keyboard/modifier-keys-combinations.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>UI Events Test: Modifier keys combinations</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://w3c.github.io/uievents/#idl-keyboardevent" />
+<meta name="assert" content="This test checks that modifier keys combinations are properly detected in 'keydown' event.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="target" tabindex="0">Target</div>
+<script>
+  const keys = {
+    "Shift": '\uE008' + 'y',
+    "Control": '\uE009' + 'y',
+    "Alt": '\uE00A' + 'y',
+    "Meta": '\uE03D' + 'y',
+  };
+
+  target.focus();
+  for (const [key, code] of Object.entries(keys)) {
+    promise_test(() => {
+      return new Promise(resolve => {
+        target.addEventListener("keydown", (event) => {
+          if (event.key != key)
+              resolve(event);
+        });
+        test_driver.send_keys(target, code);
+      }).then((event) => {
+        if (event.shiftKey) {
+          // Shift + y will send a "Y" keydown event on Chromium and Firefox, but a "y" one on WebKit.
+          assert_true(event.key == "y" || event.key == "Y");
+        } else {
+          assert_equals(event.key, "y");
+        }
+        assert_equals(event.shiftKey, key === "Shift");
+        assert_equals(event.ctrlKey, key === "Control");
+        assert_equals(event.altKey, key === "Alt");
+        assert_equals(event.metaKey, key === "Meta");
+      });
+    }, `Check sending "${key} + y" key combination`);
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js
index 8b918dc..9ce1d0d7 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js
@@ -24,6 +24,44 @@
   });
 }
 
+// Merge all audio buffers into a new big one with all the data.
+function join_buffers(buffers) {
+  assert_greater_than_equal(buffers.length, 0);
+  let total_length = 0;
+  let base_buffer = buffers[0];
+  for (const buffer of buffers) {
+    assert_not_equals(buffer, null);
+    assert_equals(buffer.sampleRate, base_buffer.sampleRate);
+    assert_equals(buffer.numberOfChannels, base_buffer.numberOfChannels);
+    total_length += buffer.length;
+  }
+
+  let result = new AudioBuffer({
+    length: total_length,
+    numberOfChannels: base_buffer.numberOfChannels,
+    sampleRate: base_buffer.sampleRate
+  });
+
+  for (let i = 0; i < base_buffer.numberOfChannels; i++) {
+    let channel = result.getChannelData(i);
+    let position = 0;
+    for (const buffer of buffers) {
+      channel.set(buffer.getChannelData(i), position);
+      position += buffer.length;
+    }
+    assert_equals(position, total_length);
+  }
+
+  return result;
+}
+
+function clone_frame(frame) {
+  return new AudioFrame({
+    timestamp: frame.timestamp,
+    buffer: join_buffers([frame.buffer])
+  });
+}
+
 promise_test(async t => {
   let sample_rate = 48000;
   let total_duration_s = 2;
@@ -55,7 +93,7 @@
     let frame_duration_s = total_duration_s / frame_count;
     let length = frame_duration_s * config.sampleRate;
     let frame = make_audio_frame(timestamp_us, config.numberOfChannels,
-                                 config.sampleRate, length);
+      config.sampleRate, length);
     encoder.encode(frame);
     timestamp_us += frame_duration_s * 1_000_000;
   }
@@ -68,3 +106,82 @@
     assert_greater_than(timestamp_us, chunk.timestamp);
   }
 }, 'Simple audio encoding');
+
+
+promise_test(async t => {
+  let sample_rate = 48000;
+  let total_duration_s = 2;
+  let frame_count = 20;
+  let input_frames = [];
+  let output_frames = [];
+
+  let decoder_init = {
+    error: t.unreached_func("Decode error"),
+    output: frame => {
+      output_frames.push(frame);
+    }
+  };
+  let decoder = new AudioDecoder(decoder_init);
+
+  let encoder_init = {
+    error: t.unreached_func("Encoder error"),
+    output: chunk => {
+      decoder.decode(chunk);
+    }
+  };
+  let encoder = new AudioEncoder(encoder_init);
+
+  let config = {
+    codec: 'opus',
+    sampleRate: sample_rate,
+    numberOfChannels: 2,
+    bitrate: 256000, //256kbit
+    // Opus header extradata.
+    // TODO(https://crbug.com/1177021) Get this data from AudioEncoder
+    description: new Uint8Array([0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64,
+      0x01, 0x02, 0x38, 0x01, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00])
+  };
+
+  encoder.configure(config);
+  decoder.configure(config);
+
+  let timestamp_us = 0;
+  const frame_duration_s = total_duration_s / frame_count;
+  const frame_length = frame_duration_s * config.sampleRate;
+  for (let i = 0; i < frame_count; i++) {
+    let frame = make_audio_frame(timestamp_us, config.numberOfChannels,
+      config.sampleRate, frame_length);
+    input_frames.push(clone_frame(frame));
+    encoder.encode(frame);
+    timestamp_us += frame_duration_s * 1_000_000;
+  }
+  await encoder.flush();
+  encoder.close();
+  await decoder.flush();
+  decoder.close();
+
+
+  let total_input = join_buffers(input_frames.map(f => f.buffer));
+  let total_output = join_buffers(output_frames.map(f => f.buffer));
+  assert_equals(total_output.numberOfChannels, 2);
+  assert_equals(total_output.sampleRate, sample_rate);
+
+  // Output can be slightly longer that the input due to padding
+  assert_greater_than_equal(total_output.length, total_input.length);
+  assert_greater_than_equal(total_output.duration, total_duration_s);
+  assert_approx_equals(total_output.duration, total_duration_s, 0.1);
+
+  // Compare waveform before and after encoding
+  for (let channel = 0; channel < total_input.numberOfChannels; channel++) {
+    let input_data = total_input.getChannelData(channel);
+    let output_data = total_output.getChannelData(channel);
+    for (let i = 0; i < total_input.length; i++) {
+      assert_approx_equals(input_data[i], output_data[i], 0.5,
+        "Difference between input and output is too large."
+        + " index: " + i
+        + " input: " + input_data[i]
+        + " output: " + output_data[i]);
+    }
+  }
+
+}, 'Encoding and decoding');
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-helper.js b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-helper.js
index 5aef0a8e..cdfe63e7 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-helper.js
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-helper.js
@@ -242,8 +242,7 @@
 async function listenForSSRCs(t, receiver) {
   while (true) {
     const ssrcs = receiver.getSynchronizationSources();
-    assert_true(Array.isArray(ssrcs));
-    if (ssrcs.length > 0) {
+    if (Array.isArray(ssrcs) && ssrcs.length > 0) {
       return ssrcs;
     }
     await new Promise(r => t.step_timeout(r, 0));
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https.html
new file mode 100644
index 0000000..7a896aa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let testName = "getLightEstimate rejects if probe is from wrong session";
+    let testFunction = (session, controller, t, sessionObjects) => new Promise((resolve) => {
+        let staleLightProbe = null;
+        let newSession = null;
+
+      function onFrame(time, frame) {
+        t.step(() => {
+          // Attempting to get a lightEstimate with a probe created for a
+          // different session should throw an exception.
+          assert_throws_dom('InvalidStateError', () => frame.getLightEstimate(staleLightProbe));
+        });
+
+        // Cleanup the new session we made and then resolve.
+        resolve(newSession.end());
+      }
+
+      // Request a default lightProbe
+      let probeInit = {reflectionFormat: session.preferredReflectionFormat };
+      session.requestLightProbe(probeInit).then((probe) => {
+        staleLightProbe = probe;
+        return session.end();
+      }).then(() => {
+        // Need to request a new session.
+        navigator.xr.test.simulateUserActivation( () => {
+          navigator.xr.requestSession('immersive-ar', {'requiredFeatures': ['light-estimation']})
+          .then((session2) => {
+
+            let glLayer = new XRWebGLLayer(session2, sessionObjects.gl);
+            glLayer.context = sessionObjects.gl;
+            // Session must have a baseLayer or frame requests will be ignored.
+            session2.updateRenderState({
+                baseLayer: glLayer
+            });
+            newSession = session2;
+            newSession.requestAnimationFrame(onFrame);
+          });
+        });
+      });
+    });
+
+    xr_session_promise_test(
+      testName,
+      testFunction,
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar',
+      {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https.html
new file mode 100644
index 0000000..499a30d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let testName =  "Cannot get XrLightEstimate from stale frame";
+    let testFunction = (session, controller, t) => new Promise((resolve) => {
+      let lightProbe = null;
+      let staleFrame = null;
+
+      function onFrame(time, frame) {
+        // Try to get the light estimate (even if it's null), it shouldn't throw.
+        let estimate = frame.getLightEstimate(lightProbe);
+        staleFrame = frame;
+
+        t.step_timeout(afterFrame, 10);
+      }
+
+      function afterFrame() {
+        t.step(() => {
+          // Attempting to call a method on the frame outside the callback that
+          // originally provided it should cause it to throw an exception.
+          assert_throws_dom('InvalidStateError', () => staleFrame.getLightEstimate(lightProbe));
+        });
+
+        resolve();
+      }
+
+      // Request a default lightProbe
+      let probeInit = {reflectionFormat: session.preferredReflectionFormat};
+      session.requestLightProbe(probeInit).then((probe) => {
+        lightProbe = probe;
+        session.requestAnimationFrame(onFrame);
+      });
+    });
+
+    xr_session_promise_test(
+      testName,
+      testFunction,
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar', {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https.html
new file mode 100644
index 0000000..68c5d84
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_asserts.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let testName = "Can get XRLightEstimates during frame";
+    let fakeDeviceInitParams = IMMERSIVE_AR_DEVICE;
+
+    let fakeEstimateCoefficients = [
+      0.01, 0.02, 0.03,
+      0.04, 0.05, 0.06,
+      0.07, 0.08, 0.09,
+      0.10, 0.11, 0.12,
+      0.13, 0.14, 0.15,
+      0.16, 0.17, 0.18,
+      0.19, 0.20, 0.21,
+      0.22, 0.23, 0.24,
+      0.25, 0.26, 0.27
+    ];
+
+    let fakeDirectionInit = { x: 1.0, y: 0.0, z: 0.0, w: 0.0 };
+    let fakeIntensityInit = { x: 0.0, y: 0.0, z: 1.0, w: 1.0 };
+
+    let testFunction = (session, controller, t) => new Promise((resolve) => {
+      let lightProbe = null;
+      function onFrameWithNoLightEstimation(time, frame) {
+        let estimate = frame.getLightEstimate(lightProbe);
+        t.step(() => {
+          assert_equals(estimate, null);
+        });
+
+        controller.setLightEstimate({
+          sphericalHarmonicsCoefficients: fakeEstimateCoefficients
+        });
+
+        requestSkipAnimationFrame(session, onFrameWithCoefficients);
+      }
+
+      function onFrameWithCoefficients(time, frame) {
+        let estimate = frame.getLightEstimate(lightProbe);
+        t.step(() => {
+          assert_not_equals(estimate, null);
+          assert_equals(estimate.sphericalHarmonicsCoefficients.length, 27);
+          assert_point_approx_equals(estimate.primaryLightDirection, { x: 0.0, y: 1.0, z: 0.0, w: 0.0 });
+          assert_point_approx_equals(estimate.primaryLightIntensity, { x: 0.0, y: 0.0, z: 0.0, w: 1.0 });
+        });
+
+        controller.setLightEstimate({
+          sphericalHarmonicsCoefficients: fakeEstimateCoefficients,
+          primaryLightDirection: fakeDirectionInit,
+        });
+
+        requestSkipAnimationFrame(session, onFrameWithDirection);
+      }
+
+      function onFrameWithDirection(time, frame) {
+        let estimate = frame.getLightEstimate(lightProbe);
+        t.step(() => {
+          assert_not_equals(estimate, null);
+          assert_equals(estimate.sphericalHarmonicsCoefficients.length, 27);
+          assert_point_approx_equals(estimate.primaryLightDirection, fakeDirectionInit);
+          assert_point_approx_equals(estimate.primaryLightIntensity, { x: 0.0, y: 0.0, z: 0.0, w: 1.0 });
+        });
+
+        controller.setLightEstimate({
+          sphericalHarmonicsCoefficients: fakeEstimateCoefficients,
+          primaryLightDirection: fakeDirectionInit,
+          primaryLightIntensity: fakeIntensityInit
+        });
+
+        requestSkipAnimationFrame(session, onFrameWithDirectionAndIntensity);
+      }
+
+      function onFrameWithDirectionAndIntensity(time, frame) {
+        let estimate = frame.getLightEstimate(lightProbe);
+        t.step(() => {
+          assert_not_equals(estimate, null);
+          assert_equals(estimate.sphericalHarmonicsCoefficients.length, 27);
+          assert_point_approx_equals(estimate.primaryLightDirection, fakeDirectionInit);
+          assert_point_approx_equals(estimate.primaryLightIntensity, fakeIntensityInit);
+        });
+
+        resolve();
+      }
+
+      // Request a default lightProbe
+      session.requestLightProbe({reflectionFormat: session.preferredReflectionFormat }).then((probe) => {
+        lightProbe = probe;
+        session.requestAnimationFrame(onFrameWithNoLightEstimation);
+      });
+    });
+
+    xr_session_promise_test(
+      testName,
+      testFunction,
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar', {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https.html
new file mode 100644
index 0000000..cc046499
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    xr_session_promise_test(
+      "getLightProbe rejects on an ended session",
+      (session, controller, t) => {
+        return session.end().then(() => {
+          return promise_rejects_dom(t, "InvalidStateError", session.requestLightProbe())
+        })
+      },
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar',
+      {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https.html
new file mode 100644
index 0000000..23fe1c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let fakeDeviceInitParams = IMMERSIVE_AR_DEVICE;
+
+    xr_session_promise_test(
+      "getLightProbe rejects if not enabled on session",
+      (session, controller, t) => promise_rejects_dom(t, "NotSupportedError", session.requestLightProbe()),
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar');
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https.html
new file mode 100644
index 0000000..074c7fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let testName = "Can create valid XRLightProbe objects";
+
+    function testFunction(session, controller, t) {
+      // Request a default lightProbe
+      let defaultProbe = session.requestLightProbe();
+      let srgba8Probe = session.requestLightProbe({reflectionFormat: "srgba8"});
+      let preferredProbe = session.requestLightProbe({reflectionFormat: session.preferredReflectionFormat });
+
+      return Promise.all([defaultProbe, srgba8Probe, preferredProbe]);
+    }
+
+    xr_session_promise_test(
+      testName,
+      testFunction,
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar', {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html
new file mode 100644
index 0000000..b46f448
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="../resources/webxr_util.js"></script>
+  <script src="../resources/webxr_test_constants.js"></script>
+
+  <script>
+    let testName = "Test that getReflectionCubeMap returns or throws appropriately without a reflection map.";
+
+    let testFunction = (session, controller, t, sessionObjects) => new Promise((resolve) => {
+        let debug = xr_debug.bind(this, 'testFunction');
+        let lightProbe1 = null;
+        let binding1 = new XRWebGLBinding(session, sessionObjects.gl);
+
+      // Request a default lightProbe
+      session.requestLightProbe({reflectionFormat: session.preferredReflectionFormat }).then((probe) => {
+        // Stash and end session.
+        lightProbe1 = probe;
+
+        debug("Querying first pair");
+        t.step(() => {
+          assert_equals(
+            binding1.getReflectionCubeMap(lightProbe1),
+            null,
+            "Active binding and light probe shouldn't throw when requesting cube map");
+        });
+
+        return session.end();
+      }).then(() => {
+        // Need to request a new session.
+        navigator.xr.test.simulateUserActivation( () => {
+          navigator.xr.requestSession('immersive-ar', { 'requiredFeatures': ['light-estimation'] })
+          .then((newSession) => {
+            let newBinding = new XRWebGLBinding(newSession, sessionObjects.gl);
+            newSession.requestLightProbe({ reflectionFormat: newSession.preferredReflectionFormat }).then((newProbe) => {
+              t.step(() => {
+                debug("Querying second pair");
+                assert_equals(
+                  newBinding.getReflectionCubeMap(newProbe),
+                  null,
+                  "Newly created binding and light probe shouldn't throw");
+
+                debug("Querying old pair");
+                assert_throws_dom(
+                  "InvalidStateError",
+                  () => binding1.getReflectionCubeMap(lightProbe1),
+                  "Binding created with an ended session should throw InvalidStateError");
+                debug("Querying mismatched pair");
+                assert_throws_dom(
+                  "InvalidStateError",
+                  () => newBinding.getReflectionCubeMap(lightProbe1),
+                  "Querying binding with a probe with a different backing session should throw InvalidStateError");
+              });
+              debug("losing context");
+
+              // Trigger a context loss and verify that we are unable to get the reflectionCubeMap.
+              let lose_context_ext = sessionObjects.gl.getExtension('WEBGL_lose_context');
+
+              sessionObjects.gl.canvas.addEventListener('webglcontextlost', (ev) => {
+                ev.preventDefault();
+
+                t.step(() => {
+                  assert_throws_dom(
+                    "InvalidStateError",
+                    () => newBinding.getReflectionCubeMap(newProbe),
+                    "Querying for reflection cube map on a binding with context loss should throw InvalidStateError");
+                });
+
+                resolve(newSession.end());
+              });
+
+              lose_context_ext.loseContext();
+            }); // Request second light probe
+          }); // Request second session
+        }); // SimulateUserActivation
+      }); // .then on session end
+    }); // testFunction
+
+    xr_session_promise_test(
+      testName,
+      testFunction,
+      IMMERSIVE_AR_DEVICE,
+      'immersive-ar',
+      {'requiredFeatures': ['light-estimation']});
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/fast/table/large-col-span-crash.html b/third_party/blink/web_tests/fast/table/large-col-span-crash.html
index b440470..6fc7362 100644
--- a/third_party/blink/web_tests/fast/table/large-col-span-crash.html
+++ b/third_party/blink/web_tests/fast/table/large-col-span-crash.html
@@ -10,7 +10,7 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/check-layout-th.js"></script>
-<table data-expected-width=100 data-expected-height=100>
+<table data-expected-width=2004 data-expected-height=100>
   <col span="4294967295">
   <col>
 </table>
diff --git a/third_party/blink/web_tests/http/tests/loading/window-open-onblur-reentrancy-expected.txt b/third_party/blink/web_tests/http/tests/loading/window-open-onblur-reentrancy-expected.txt
index 54014cf..658f9d6 100644
--- a/third_party/blink/web_tests/http/tests/loading/window-open-onblur-reentrancy-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/window-open-onblur-reentrancy-expected.txt
@@ -9,5 +9,6 @@
 frame "i" - didHandleOnloadEventsForFrame
 frame "i" - didFinishLoadForFrame
 main frame - didFinishDocumentLoadForFrame
+main frame - didCommitLoadForFrame
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe-expected.txt b/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe-expected.txt
index a1d0ffb..6def51f 100644
--- a/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe-expected.txt
@@ -1,6 +1,3 @@
 This test verifies that setting the iframe.src through javascript to # does not add a history item. If the test passes you'll see only one history item.
 
-============== Back Forward List ==============
-curr->  http://127.0.0.1:8000/navigation/lockedhistory-iframe.html
-            http://127.0.0.1:8000/navigation/lockedhistory-iframe.html# (in frame "<!--framePath //<!--frame0-->-->")
-===============================================
+ PASS
diff --git a/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe.html b/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe.html
index fb4420a2..f0f5c50 100644
--- a/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe.html
+++ b/third_party/blink/web_tests/http/tests/navigation/lockedhistory-iframe.html
@@ -2,7 +2,6 @@
 <head>
 <script src="resources/document-location.js"></script>
 <script type="text/javascript">
-testRunner.dumpBackForwardList();
 testRunner.dumpAsText();
 testRunner.waitUntilDone();
 
@@ -15,9 +14,14 @@
 <body>
 <p>This test verifies that setting the iframe.src through javascript to # does not add a history item. If the test passes you'll see only one history item.</p>
 <script type="text/javascript">
+let historyLength = history.length;
 myFrame = document.createElement("iframe");    
 myFrame.src = "javascript:document.write('<script type=text/javascript>window.parent.myCallbackFunction();<\/script>'); document.close()";
-myFrame.onload = () => testRunner.notifyDone();
+myFrame.onload = () => {
+  document.body.appendChild(document.createTextNode(
+      historyLength == history.length ? "PASS" : "FAIL"));
+  testRunner.notifyDone();
+}
 document.body.appendChild(myFrame);
 </script>
 </body>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 5375ce2..6bfae355 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1011,6 +1011,7 @@
     method lineTo
     method measureText
     method moveTo
+    method perspective
     method putImageData
     method quadraticCurveTo
     method rect
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-constants.js b/third_party/blink/web_tests/http/tests/wasm/resources/wasm-constants.js
deleted file mode 100644
index 2656768e..0000000
--- a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-constants.js
+++ /dev/null
@@ -1,447 +0,0 @@
-// Copyright 2017 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-function bytes() {
-  var buffer = new ArrayBuffer(arguments.length);
-  var view = new Uint8Array(buffer);
-  for (var i = 0; i < arguments.length; i++) {
-    var val = arguments[i];
-    if ((typeof val) == "string") val = val.charCodeAt(0);
-    view[i] = val | 0;
-  }
-  return buffer;
-}
-
-// Header declaration constants
-var kWasmH0 = 0;
-var kWasmH1 = 0x61;
-var kWasmH2 = 0x73;
-var kWasmH3 = 0x6d;
-
-var kWasmV0 = 0x1;
-var kWasmV1 = 0;
-var kWasmV2 = 0;
-var kWasmV3 = 0;
-
-var kHeaderSize = 8;
-var kPageSize = 65536;
-var kSpecMaxPages = 65535;
-
-function bytesWithHeader() {
-  var buffer = new ArrayBuffer(kHeaderSize + arguments.length);
-  var view = new Uint8Array(buffer);
-  view[0] = kWasmH0;
-  view[1] = kWasmH1;
-  view[2] = kWasmH2;
-  view[3] = kWasmH3;
-  view[4] = kWasmV0;
-  view[5] = kWasmV1;
-  view[6] = kWasmV2;
-  view[7] = kWasmV3;
-  for (var i = 0; i < arguments.length; i++) {
-    var val = arguments[i];
-    if ((typeof val) == "string") val = val.charCodeAt(0);
-    view[kHeaderSize + i] = val | 0;
-  }
-  return buffer;
-}
-
-let kDeclNoLocals = 0;
-
-// Section declaration constants
-let kUnknownSectionCode = 0;
-let kTypeSectionCode = 1;        // Function signature declarations
-let kImportSectionCode = 2;      // Import declarations
-let kFunctionSectionCode = 3;    // Function declarations
-let kTableSectionCode = 4;       // Indirect function table and other tables
-let kMemorySectionCode = 5;      // Memory attributes
-let kGlobalSectionCode = 6;      // Global declarations
-let kExportSectionCode = 7;      // Exports
-let kStartSectionCode = 8;       // Start function declaration
-let kElementSectionCode = 9;     // Elements section
-let kCodeSectionCode = 10;       // Function code
-let kDataSectionCode = 11;       // Data segments
-let kNameSectionCode = 12;       // Name section (encoded as string)
-let kExceptionSectionCode = 13;  // Exception section (must appear before code section)
-
-// Name section types
-let kModuleNameCode = 0;
-let kFunctionNamesCode = 1;
-let kLocalNamesCode = 2;
-
-let kWasmFunctionTypeForm = 0x60;
-let kWasmAnyFunctionTypeForm = 0x70;
-
-let kHasMaximumFlag = 1;
-
-// Function declaration flags
-let kDeclFunctionName   = 0x01;
-let kDeclFunctionImport = 0x02;
-let kDeclFunctionLocals = 0x04;
-let kDeclFunctionExport = 0x08;
-
-// Local types
-let kWasmStmt = 0x40;
-let kWasmI32 = 0x7f;
-let kWasmI64 = 0x7e;
-let kWasmF32 = 0x7d;
-let kWasmF64 = 0x7c;
-let kWasmS128  = 0x7b;
-let kWasmAnyRef = 0x6f;
-
-let kExternalFunction = 0;
-let kExternalTable = 1;
-let kExternalMemory = 2;
-let kExternalGlobal = 3;
-
-let kTableZero = 0;
-let kMemoryZero = 0;
-
-// Useful signatures
-let kSig_i_i = makeSig([kWasmI32], [kWasmI32]);
-let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
-let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
-let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
-let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
-let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
-let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
-let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
-let kSig_v_v = makeSig([], []);
-let kSig_i_v = makeSig([], [kWasmI32]);
-let kSig_l_v = makeSig([], [kWasmI64]);
-let kSig_f_v = makeSig([], [kWasmF32]);
-let kSig_d_v = makeSig([], [kWasmF64]);
-let kSig_v_i = makeSig([kWasmI32], []);
-let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []);
-let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []);
-let kSig_v_l = makeSig([kWasmI64], []);
-let kSig_v_d = makeSig([kWasmF64], []);
-let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
-let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
-let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
-let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
-let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
-let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
-let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
-let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
-
-let kSig_v_f = makeSig([kWasmF32], []);
-let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
-let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
-let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]);
-let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]);
-let kSig_v_r = makeSig([kWasmAnyRef], []);
-let kSig_r_v = makeSig([], [kWasmAnyRef]);
-
-function makeSig(params, results) {
-  return {params: params, results: results};
-}
-
-function makeSig_v_x(x) {
-  return makeSig([x], []);
-}
-
-function makeSig_v_xx(x) {
-  return makeSig([x, x], []);
-}
-
-function makeSig_r_v(r) {
-  return makeSig([], [r]);
-}
-
-function makeSig_r_x(r, x) {
-  return makeSig([x], [r]);
-}
-
-function makeSig_r_xx(r, x) {
-  return makeSig([x, x], [r]);
-}
-
-// Opcodes
-let kExprUnreachable = 0x00;
-let kExprNop = 0x01;
-let kExprBlock = 0x02;
-let kExprLoop = 0x03;
-let kExprIf = 0x04;
-let kExprElse = 0x05;
-let kExprTry = 0x06;
-let kExprCatch = 0x07;
-let kExprThrow = 0x08;
-let kExprEnd = 0x0b;
-let kExprBr = 0x0c;
-let kExprBrIf = 0x0d;
-let kExprBrTable = 0x0e;
-let kExprReturn = 0x0f;
-let kExprCallFunction = 0x10;
-let kExprCallIndirect = 0x11;
-let kExprDrop = 0x1a;
-let kExprSelect = 0x1b;
-let kExprGetLocal = 0x20;
-let kExprSetLocal = 0x21;
-let kExprTeeLocal = 0x22;
-let kExprGetGlobal = 0x23;
-let kExprSetGlobal = 0x24;
-let kExprI32Const = 0x41;
-let kExprI64Const = 0x42;
-let kExprF32Const = 0x43;
-let kExprF64Const = 0x44;
-let kExprRefNull = 0xd0;
-let kExprI32LoadMem = 0x28;
-let kExprI64LoadMem = 0x29;
-let kExprF32LoadMem = 0x2a;
-let kExprF64LoadMem = 0x2b;
-let kExprI32LoadMem8S = 0x2c;
-let kExprI32LoadMem8U = 0x2d;
-let kExprI32LoadMem16S = 0x2e;
-let kExprI32LoadMem16U = 0x2f;
-let kExprI64LoadMem8S = 0x30;
-let kExprI64LoadMem8U = 0x31;
-let kExprI64LoadMem16S = 0x32;
-let kExprI64LoadMem16U = 0x33;
-let kExprI64LoadMem32S = 0x34;
-let kExprI64LoadMem32U = 0x35;
-let kExprI32StoreMem = 0x36;
-let kExprI64StoreMem = 0x37;
-let kExprF32StoreMem = 0x38;
-let kExprF64StoreMem = 0x39;
-let kExprI32StoreMem8 = 0x3a;
-let kExprI32StoreMem16 = 0x3b;
-let kExprI64StoreMem8 = 0x3c;
-let kExprI64StoreMem16 = 0x3d;
-let kExprI64StoreMem32 = 0x3e;
-let kExprMemorySize = 0x3f;
-let kExprGrowMemory = 0x40;
-let kExprI32Eqz = 0x45;
-let kExprI32Eq = 0x46;
-let kExprI32Ne = 0x47;
-let kExprI32LtS = 0x48;
-let kExprI32LtU = 0x49;
-let kExprI32GtS = 0x4a;
-let kExprI32GtU = 0x4b;
-let kExprI32LeS = 0x4c;
-let kExprI32LeU = 0x4d;
-let kExprI32GeS = 0x4e;
-let kExprI32GeU = 0x4f;
-let kExprI64Eqz = 0x50;
-let kExprI64Eq = 0x51;
-let kExprI64Ne = 0x52;
-let kExprI64LtS = 0x53;
-let kExprI64LtU = 0x54;
-let kExprI64GtS = 0x55;
-let kExprI64GtU = 0x56;
-let kExprI64LeS = 0x57;
-let kExprI64LeU = 0x58;
-let kExprI64GeS = 0x59;
-let kExprI64GeU = 0x5a;
-let kExprF32Eq = 0x5b;
-let kExprF32Ne = 0x5c;
-let kExprF32Lt = 0x5d;
-let kExprF32Gt = 0x5e;
-let kExprF32Le = 0x5f;
-let kExprF32Ge = 0x60;
-let kExprF64Eq = 0x61;
-let kExprF64Ne = 0x62;
-let kExprF64Lt = 0x63;
-let kExprF64Gt = 0x64;
-let kExprF64Le = 0x65;
-let kExprF64Ge = 0x66;
-let kExprRefIsNull = 0xd1;
-let kExprI32Clz = 0x67;
-let kExprI32Ctz = 0x68;
-let kExprI32Popcnt = 0x69;
-let kExprI32Add = 0x6a;
-let kExprI32Sub = 0x6b;
-let kExprI32Mul = 0x6c;
-let kExprI32DivS = 0x6d;
-let kExprI32DivU = 0x6e;
-let kExprI32RemS = 0x6f;
-let kExprI32RemU = 0x70;
-let kExprI32And = 0x71;
-let kExprI32Ior = 0x72;
-let kExprI32Xor = 0x73;
-let kExprI32Shl = 0x74;
-let kExprI32ShrS = 0x75;
-let kExprI32ShrU = 0x76;
-let kExprI32Rol = 0x77;
-let kExprI32Ror = 0x78;
-let kExprI64Clz = 0x79;
-let kExprI64Ctz = 0x7a;
-let kExprI64Popcnt = 0x7b;
-let kExprI64Add = 0x7c;
-let kExprI64Sub = 0x7d;
-let kExprI64Mul = 0x7e;
-let kExprI64DivS = 0x7f;
-let kExprI64DivU = 0x80;
-let kExprI64RemS = 0x81;
-let kExprI64RemU = 0x82;
-let kExprI64And = 0x83;
-let kExprI64Ior = 0x84;
-let kExprI64Xor = 0x85;
-let kExprI64Shl = 0x86;
-let kExprI64ShrS = 0x87;
-let kExprI64ShrU = 0x88;
-let kExprI64Rol = 0x89;
-let kExprI64Ror = 0x8a;
-let kExprF32Abs = 0x8b;
-let kExprF32Neg = 0x8c;
-let kExprF32Ceil = 0x8d;
-let kExprF32Floor = 0x8e;
-let kExprF32Trunc = 0x8f;
-let kExprF32NearestInt = 0x90;
-let kExprF32Sqrt = 0x91;
-let kExprF32Add = 0x92;
-let kExprF32Sub = 0x93;
-let kExprF32Mul = 0x94;
-let kExprF32Div = 0x95;
-let kExprF32Min = 0x96;
-let kExprF32Max = 0x97;
-let kExprF32CopySign = 0x98;
-let kExprF64Abs = 0x99;
-let kExprF64Neg = 0x9a;
-let kExprF64Ceil = 0x9b;
-let kExprF64Floor = 0x9c;
-let kExprF64Trunc = 0x9d;
-let kExprF64NearestInt = 0x9e;
-let kExprF64Sqrt = 0x9f;
-let kExprF64Add = 0xa0;
-let kExprF64Sub = 0xa1;
-let kExprF64Mul = 0xa2;
-let kExprF64Div = 0xa3;
-let kExprF64Min = 0xa4;
-let kExprF64Max = 0xa5;
-let kExprF64CopySign = 0xa6;
-let kExprI32ConvertI64 = 0xa7;
-let kExprI32SConvertF32 = 0xa8;
-let kExprI32UConvertF32 = 0xa9;
-let kExprI32SConvertF64 = 0xaa;
-let kExprI32UConvertF64 = 0xab;
-let kExprI64SConvertI32 = 0xac;
-let kExprI64UConvertI32 = 0xad;
-let kExprI64SConvertF32 = 0xae;
-let kExprI64UConvertF32 = 0xaf;
-let kExprI64SConvertF64 = 0xb0;
-let kExprI64UConvertF64 = 0xb1;
-let kExprF32SConvertI32 = 0xb2;
-let kExprF32UConvertI32 = 0xb3;
-let kExprF32SConvertI64 = 0xb4;
-let kExprF32UConvertI64 = 0xb5;
-let kExprF32ConvertF64 = 0xb6;
-let kExprF64SConvertI32 = 0xb7;
-let kExprF64UConvertI32 = 0xb8;
-let kExprF64SConvertI64 = 0xb9;
-let kExprF64UConvertI64 = 0xba;
-let kExprF64ConvertF32 = 0xbb;
-let kExprI32ReinterpretF32 = 0xbc;
-let kExprI64ReinterpretF64 = 0xbd;
-let kExprF32ReinterpretI32 = 0xbe;
-let kExprF64ReinterpretI64 = 0xbf;
-
-// Prefix opcodes
-let kAtomicPrefix = 0xfe;
-
-let kExprI32AtomicLoad = 0x10;
-let kExprI32AtomicLoad8U = 0x12;
-let kExprI32AtomicLoad16U = 0x13;
-let kExprI32AtomicStore = 0x17;
-let kExprI32AtomicStore8U = 0x19;
-let kExprI32AtomicStore16U = 0x1a;
-let kExprI32AtomicAdd = 0x1e;
-let kExprI32AtomicAdd8U = 0x20;
-let kExprI32AtomicAdd16U = 0x21;
-let kExprI32AtomicSub = 0x25;
-let kExprI32AtomicSub8U = 0x27;
-let kExprI32AtomicSub16U = 0x28;
-let kExprI32AtomicAnd = 0x2c;
-let kExprI32AtomicAnd8U = 0x2e;
-let kExprI32AtomicAnd16U = 0x2f;
-let kExprI32AtomicOr = 0x33;
-let kExprI32AtomicOr8U = 0x35;
-let kExprI32AtomicOr16U = 0x36;
-let kExprI32AtomicXor = 0x3a;
-let kExprI32AtomicXor8U = 0x3c;
-let kExprI32AtomicXor16U = 0x3d;
-let kExprI32AtomicExchange = 0x41;
-let kExprI32AtomicExchange8U = 0x43;
-let kExprI32AtomicExchange16U = 0x44;
-let kExprI32AtomicCompareExchange = 0x48
-let kExprI32AtomicCompareExchange8U = 0x4a
-let kExprI32AtomicCompareExchange16U = 0x4b
-
-let kTrapUnreachable          = 0;
-let kTrapMemOutOfBounds       = 1;
-let kTrapDivByZero            = 2;
-let kTrapDivUnrepresentable   = 3;
-let kTrapRemByZero            = 4;
-let kTrapFloatUnrepresentable = 5;
-let kTrapFuncInvalid          = 6;
-let kTrapFuncSigMismatch      = 7;
-let kTrapTypeError            = 8;
-
-let kTrapMsgs = [
-  "unreachable",
-  "memory access out of bounds",
-  "divide by zero",
-  "divide result unrepresentable",
-  "remainder by zero",
-  "float unrepresentable in integer range",
-  "invalid index into function table",
-  "function signature mismatch",
-  "wasm function signature contains illegal type"
-];
-
-function assertTraps(trap, code) {
-  try {
-    if (typeof code === 'function') {
-      code();
-    } else {
-      eval(code);
-    }
-  } catch (e) {
-    assertEquals('object', typeof e);
-    assertEquals(kTrapMsgs[trap], e.message);
-    // Success.
-    return;
-  }
-  throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
-}
-
-function assertWasmThrows(runtime_id, values, code) {
-  try {
-    if (typeof code === 'function') {
-      code();
-    } else {
-      eval(code);
-    }
-  } catch (e) {
-    assertTrue(e instanceof WebAssembly.RuntimeError);
-    var e_runtime_id = e['WasmExceptionRuntimeId'];
-    assertEquals(e_runtime_id, runtime_id);
-    assertTrue(Number.isInteger(e_runtime_id));
-    var e_values = e['WasmExceptionValues'];
-    assertEquals(values.length, e_values.length);
-    for (i = 0; i < values.length; ++i) {
-      assertEquals(values[i], e_values[i]);
-    }
-    // Success.
-    return;
-  }
-  throw new MjsUnitAssertionError('Did not throw expected: ' + runtime_id + values);
-}
-
-function wasmI32Const(val) {
-  let bytes = [kExprI32Const];
-  for (let i = 0; i < 4; ++i) {
-    bytes.push(0x80 | ((val >> (7 * i)) & 0x7f));
-  }
-  bytes.push((val >> (7 * 4)) & 0x7f);
-  return bytes;
-}
-
-function wasmF32Const(f) {
-  return [kExprF32Const].concat(Array.from(new Uint8Array((new Float32Array([f])).buffer)));
-}
-
-function wasmF64Const(f) {
-  return [kExprF64Const].concat(Array.from(new Uint8Array((new Float64Array([f])).buffer)));
-}
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-module-builder.js b/third_party/blink/web_tests/http/tests/wasm/resources/wasm-module-builder.js
index c787c280..95e88cac 100644
--- a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-module-builder.js
+++ b/third_party/blink/web_tests/http/tests/wasm/resources/wasm-module-builder.js
@@ -1,46 +1,827 @@
-// Copyright 2017 the V8 project authors. All rights reserved.
+// Copyright 2021 the V8 project authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Used for encoding f32 and double constants to bits.
-let __buffer = new ArrayBuffer(8);
-let byte_view = new Int8Array(__buffer);
-let f32_view = new Float32Array(__buffer);
-let f64_view = new Float64Array(__buffer);
+let byte_view = new Uint8Array(8);
+let data_view = new DataView(byte_view.buffer);
 
-class Binary extends Array {
+// The bytes function receives one of
+//  - several arguments, each of which is either a number or a string of length
+//    1; if it's a string, the charcode of the contained character is used.
+//  - a single array argument containing the actual arguments
+//  - a single string; the returned buffer will contain the char codes of all
+//    contained characters.
+function bytes(...input) {
+  if (input.length == 1 && typeof input[0] == 'array') input = input[0];
+  if (input.length == 1 && typeof input[0] == 'string') {
+    let len = input[0].length;
+    let view = new Uint8Array(len);
+    for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i);
+    return view.buffer;
+  }
+  let view = new Uint8Array(input.length);
+  for (let i = 0; i < input.length; i++) {
+    let val = input[i];
+    if (typeof val == 'string') {
+      assertEquals(1, val.length, 'string inputs must have length 1');
+      val = val.charCodeAt(0);
+    }
+    view[i] = val | 0;
+  }
+  return view.buffer;
+}
+
+// Header declaration constants
+var kWasmH0 = 0;
+var kWasmH1 = 0x61;
+var kWasmH2 = 0x73;
+var kWasmH3 = 0x6d;
+
+var kWasmV0 = 0x1;
+var kWasmV1 = 0;
+var kWasmV2 = 0;
+var kWasmV3 = 0;
+
+var kHeaderSize = 8;
+var kPageSize = 65536;
+var kSpecMaxPages = 65536;
+var kMaxVarInt32Size = 5;
+var kMaxVarInt64Size = 10;
+
+let kDeclNoLocals = 0;
+
+// Section declaration constants
+let kUnknownSectionCode = 0;
+let kTypeSectionCode = 1;        // Function signature declarations
+let kImportSectionCode = 2;      // Import declarations
+let kFunctionSectionCode = 3;    // Function declarations
+let kTableSectionCode = 4;       // Indirect function table and other tables
+let kMemorySectionCode = 5;      // Memory attributes
+let kGlobalSectionCode = 6;      // Global declarations
+let kExportSectionCode = 7;      // Exports
+let kStartSectionCode = 8;       // Start function declaration
+let kElementSectionCode = 9;     // Elements section
+let kCodeSectionCode = 10;       // Function code
+let kDataSectionCode = 11;       // Data segments
+let kDataCountSectionCode = 12;  // Data segment count (between Element & Code)
+let kExceptionSectionCode = 13;  // Exception section (between Memory & Global)
+
+// Name section types
+let kModuleNameCode = 0;
+let kFunctionNamesCode = 1;
+let kLocalNamesCode = 2;
+
+let kWasmFunctionTypeForm = 0x60;
+let kWasmStructTypeForm = 0x5f;
+let kWasmArrayTypeForm = 0x5e;
+
+let kLimitsNoMaximum = 0x00;
+let kLimitsWithMaximum = 0x01;
+let kLimitsSharedNoMaximum = 0x02;
+let kLimitsSharedWithMaximum = 0x03;
+let kLimitsMemory64NoMaximum = 0x04;
+let kLimitsMemory64WithMaximum = 0x05;
+
+// Segment flags
+let kActiveNoIndex = 0;
+let kPassive = 1;
+let kActiveWithIndex = 2;
+let kDeclarative = 3;
+let kPassiveWithElements = 5;
+let kDeclarativeWithElements = 7;
+
+// Function declaration flags
+let kDeclFunctionName   = 0x01;
+let kDeclFunctionImport = 0x02;
+let kDeclFunctionLocals = 0x04;
+let kDeclFunctionExport = 0x08;
+
+// Value types and related
+let kWasmStmt = 0x40;
+let kWasmI32 = 0x7f;
+let kWasmI64 = 0x7e;
+let kWasmF32 = 0x7d;
+let kWasmF64 = 0x7c;
+let kWasmS128 = 0x7b;
+let kWasmI8 = 0x7a;
+let kWasmI16 = 0x79;
+let kWasmFuncRef = 0x70;
+let kWasmAnyFunc = kWasmFuncRef; // Alias named as in the JS API spec
+let kWasmExternRef = 0x6f;
+let kWasmAnyRef = 0x6e;
+let kWasmEqRef = 0x6d;
+let kWasmOptRef = 0x6c;
+let kWasmRef = 0x6b;
+function wasmOptRefType(index) { return {opcode: kWasmOptRef, index: index}; }
+function wasmRefType(index) { return {opcode: kWasmRef, index: index}; }
+let kWasmI31Ref = 0x6a;
+let kWasmRtt = 0x69;
+function wasmRtt(index, depth) {
+  return {opcode: kWasmRtt, index: index, depth: depth};
+}
+
+let kExternalFunction = 0;
+let kExternalTable = 1;
+let kExternalMemory = 2;
+let kExternalGlobal = 3;
+let kExternalException = 4;
+
+let kTableZero = 0;
+let kMemoryZero = 0;
+let kSegmentZero = 0;
+
+let kExceptionAttribute = 0;
+
+// Useful signatures
+let kSig_i_i = makeSig([kWasmI32], [kWasmI32]);
+let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
+let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
+let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
+let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
+let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
+let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
+let kSig_v_v = makeSig([], []);
+let kSig_i_v = makeSig([], [kWasmI32]);
+let kSig_l_v = makeSig([], [kWasmI64]);
+let kSig_f_v = makeSig([], [kWasmF32]);
+let kSig_d_v = makeSig([], [kWasmF64]);
+let kSig_v_i = makeSig([kWasmI32], []);
+let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []);
+let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_v_l = makeSig([kWasmI64], []);
+let kSig_v_d = makeSig([kWasmF64], []);
+let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
+let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
+let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
+let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
+let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
+let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
+let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
+let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
+
+let kSig_v_f = makeSig([kWasmF32], []);
+let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
+let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
+let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
+let kSig_r_r = makeSig([kWasmExternRef], [kWasmExternRef]);
+let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]);
+let kSig_i_r = makeSig([kWasmExternRef], [kWasmI32]);
+let kSig_v_r = makeSig([kWasmExternRef], []);
+let kSig_v_a = makeSig([kWasmAnyFunc], []);
+let kSig_v_rr = makeSig([kWasmExternRef, kWasmExternRef], []);
+let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
+let kSig_r_v = makeSig([], [kWasmExternRef]);
+let kSig_a_v = makeSig([], [kWasmAnyFunc]);
+let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
+let kSig_s_i = makeSig([kWasmI32], [kWasmS128]);
+let kSig_i_s = makeSig([kWasmS128], [kWasmI32]);
+
+function makeSig(params, results) {
+  return {params: params, results: results};
+}
+
+function makeSig_v_x(x) {
+  return makeSig([x], []);
+}
+
+function makeSig_v_xx(x) {
+  return makeSig([x, x], []);
+}
+
+function makeSig_r_v(r) {
+  return makeSig([], [r]);
+}
+
+function makeSig_r_x(r, x) {
+  return makeSig([x], [r]);
+}
+
+function makeSig_r_xx(r, x) {
+  return makeSig([x, x], [r]);
+}
+
+// Opcodes
+const kWasmOpcodes = {
+  'Unreachable': 0x00,
+  'Nop': 0x01,
+  'Block': 0x02,
+  'Loop': 0x03,
+  'If': 0x04,
+  'Else': 0x05,
+  'Try': 0x06,
+  'Catch': 0x07,
+  'Throw': 0x08,
+  'Rethrow': 0x09,
+  'Unwind': 0x0a,
+  'End': 0x0b,
+  'Br': 0x0c,
+  'BrIf': 0x0d,
+  'BrTable': 0x0e,
+  'Return': 0x0f,
+  'CallFunction': 0x10,
+  'CallIndirect': 0x11,
+  'ReturnCall': 0x12,
+  'ReturnCallIndirect': 0x13,
+  'CallRef': 0x14,
+  'ReturnCallRef': 0x15,
+  'Let': 0x17,
+  'Delegate': 0x18,
+  'Drop': 0x1a,
+  'Select': 0x1b,
+  'SelectWithType': 0x1c,
+  'LocalGet': 0x20,
+  'LocalSet': 0x21,
+  'LocalTee': 0x22,
+  'GlobalGet': 0x23,
+  'GlobalSet': 0x24,
+  'TableGet': 0x25,
+  'TableSet': 0x26,
+  'I32LoadMem': 0x28,
+  'I64LoadMem': 0x29,
+  'F32LoadMem': 0x2a,
+  'F64LoadMem': 0x2b,
+  'I32LoadMem8S': 0x2c,
+  'I32LoadMem8U': 0x2d,
+  'I32LoadMem16S': 0x2e,
+  'I32LoadMem16U': 0x2f,
+  'I64LoadMem8S': 0x30,
+  'I64LoadMem8U': 0x31,
+  'I64LoadMem16S': 0x32,
+  'I64LoadMem16U': 0x33,
+  'I64LoadMem32S': 0x34,
+  'I64LoadMem32U': 0x35,
+  'I32StoreMem': 0x36,
+  'I64StoreMem': 0x37,
+  'F32StoreMem': 0x38,
+  'F64StoreMem': 0x39,
+  'I32StoreMem8': 0x3a,
+  'I32StoreMem16': 0x3b,
+  'I64StoreMem8': 0x3c,
+  'I64StoreMem16': 0x3d,
+  'I64StoreMem32': 0x3e,
+  'MemorySize': 0x3f,
+  'MemoryGrow': 0x40,
+  'I32Const': 0x41,
+  'I64Const': 0x42,
+  'F32Const': 0x43,
+  'F64Const': 0x44,
+  'I32Eqz': 0x45,
+  'I32Eq': 0x46,
+  'I32Ne': 0x47,
+  'I32LtS': 0x48,
+  'I32LtU': 0x49,
+  'I32GtS': 0x4a,
+  'I32GtU': 0x4b,
+  'I32LeS': 0x4c,
+  'I32LeU': 0x4d,
+  'I32GeS': 0x4e,
+  'I32GeU': 0x4f,
+  'I64Eqz': 0x50,
+  'I64Eq': 0x51,
+  'I64Ne': 0x52,
+  'I64LtS': 0x53,
+  'I64LtU': 0x54,
+  'I64GtS': 0x55,
+  'I64GtU': 0x56,
+  'I64LeS': 0x57,
+  'I64LeU': 0x58,
+  'I64GeS': 0x59,
+  'I64GeU': 0x5a,
+  'F32Eq': 0x5b,
+  'F32Ne': 0x5c,
+  'F32Lt': 0x5d,
+  'F32Gt': 0x5e,
+  'F32Le': 0x5f,
+  'F32Ge': 0x60,
+  'F64Eq': 0x61,
+  'F64Ne': 0x62,
+  'F64Lt': 0x63,
+  'F64Gt': 0x64,
+  'F64Le': 0x65,
+  'F64Ge': 0x66,
+  'I32Clz': 0x67,
+  'I32Ctz': 0x68,
+  'I32Popcnt': 0x69,
+  'I32Add': 0x6a,
+  'I32Sub': 0x6b,
+  'I32Mul': 0x6c,
+  'I32DivS': 0x6d,
+  'I32DivU': 0x6e,
+  'I32RemS': 0x6f,
+  'I32RemU': 0x70,
+  'I32And': 0x71,
+  'I32Ior': 0x72,
+  'I32Xor': 0x73,
+  'I32Shl': 0x74,
+  'I32ShrS': 0x75,
+  'I32ShrU': 0x76,
+  'I32Rol': 0x77,
+  'I32Ror': 0x78,
+  'I64Clz': 0x79,
+  'I64Ctz': 0x7a,
+  'I64Popcnt': 0x7b,
+  'I64Add': 0x7c,
+  'I64Sub': 0x7d,
+  'I64Mul': 0x7e,
+  'I64DivS': 0x7f,
+  'I64DivU': 0x80,
+  'I64RemS': 0x81,
+  'I64RemU': 0x82,
+  'I64And': 0x83,
+  'I64Ior': 0x84,
+  'I64Xor': 0x85,
+  'I64Shl': 0x86,
+  'I64ShrS': 0x87,
+  'I64ShrU': 0x88,
+  'I64Rol': 0x89,
+  'I64Ror': 0x8a,
+  'F32Abs': 0x8b,
+  'F32Neg': 0x8c,
+  'F32Ceil': 0x8d,
+  'F32Floor': 0x8e,
+  'F32Trunc': 0x8f,
+  'F32NearestInt': 0x90,
+  'F32Sqrt': 0x91,
+  'F32Add': 0x92,
+  'F32Sub': 0x93,
+  'F32Mul': 0x94,
+  'F32Div': 0x95,
+  'F32Min': 0x96,
+  'F32Max': 0x97,
+  'F32CopySign': 0x98,
+  'F64Abs': 0x99,
+  'F64Neg': 0x9a,
+  'F64Ceil': 0x9b,
+  'F64Floor': 0x9c,
+  'F64Trunc': 0x9d,
+  'F64NearestInt': 0x9e,
+  'F64Sqrt': 0x9f,
+  'F64Add': 0xa0,
+  'F64Sub': 0xa1,
+  'F64Mul': 0xa2,
+  'F64Div': 0xa3,
+  'F64Min': 0xa4,
+  'F64Max': 0xa5,
+  'F64CopySign': 0xa6,
+  'I32ConvertI64': 0xa7,
+  'I32SConvertF32': 0xa8,
+  'I32UConvertF32': 0xa9,
+  'I32SConvertF64': 0xaa,
+  'I32UConvertF64': 0xab,
+  'I64SConvertI32': 0xac,
+  'I64UConvertI32': 0xad,
+  'I64SConvertF32': 0xae,
+  'I64UConvertF32': 0xaf,
+  'I64SConvertF64': 0xb0,
+  'I64UConvertF64': 0xb1,
+  'F32SConvertI32': 0xb2,
+  'F32UConvertI32': 0xb3,
+  'F32SConvertI64': 0xb4,
+  'F32UConvertI64': 0xb5,
+  'F32ConvertF64': 0xb6,
+  'F64SConvertI32': 0xb7,
+  'F64UConvertI32': 0xb8,
+  'F64SConvertI64': 0xb9,
+  'F64UConvertI64': 0xba,
+  'F64ConvertF32': 0xbb,
+  'I32ReinterpretF32': 0xbc,
+  'I64ReinterpretF64': 0xbd,
+  'F32ReinterpretI32': 0xbe,
+  'F64ReinterpretI64': 0xbf,
+  'I32SExtendI8': 0xc0,
+  'I32SExtendI16': 0xc1,
+  'I64SExtendI8': 0xc2,
+  'I64SExtendI16': 0xc3,
+  'I64SExtendI32': 0xc4,
+  'RefNull': 0xd0,
+  'RefIsNull': 0xd1,
+  'RefFunc': 0xd2
+};
+
+function defineWasmOpcode(name, value) {
+  if (globalThis.kWasmOpcodeNames === undefined) {
+    globalThis.kWasmOpcodeNames = {};
+  }
+  Object.defineProperty(globalThis, name, {value: value});
+  if (globalThis.kWasmOpcodeNames[value] !== undefined) {
+    throw new Error(`Duplicate wasm opcode: ${value}. Previous name: ${
+        globalThis.kWasmOpcodeNames[value]}, new name: ${name}`);
+  }
+  globalThis.kWasmOpcodeNames[value] = name;
+}
+for (let name in kWasmOpcodes) {
+  defineWasmOpcode(`kExpr${name}`, kWasmOpcodes[name]);
+}
+
+// Prefix opcodes
+const kPrefixOpcodes = {
+  'GC': 0xfb,
+  'Numeric': 0xfc,
+  'Simd': 0xfd,
+  'Atomic': 0xfe
+};
+for (let prefix in kPrefixOpcodes) {
+  defineWasmOpcode(`k${prefix}Prefix`, kPrefixOpcodes[prefix]);
+}
+
+// GC opcodes
+let kExprStructNewWithRtt = 0x01;
+let kExprStructNewDefault = 0x02;
+let kExprStructGet = 0x03;
+let kExprStructGetS = 0x04;
+let kExprStructGetU = 0x05;
+let kExprStructSet = 0x06;
+let kExprArrayNewWithRtt = 0x11;
+let kExprArrayNewDefault = 0x12;
+let kExprArrayGet = 0x13;
+let kExprArrayGetS = 0x14;
+let kExprArrayGetU = 0x15;
+let kExprArraySet = 0x16;
+let kExprArrayLen = 0x17;
+let kExprI31New = 0x20;
+let kExprI31GetS = 0x21;
+let kExprI31GetU = 0x22;
+let kExprRttCanon = 0x30;
+let kExprRttSub = 0x31;
+let kExprRefTest = 0x40;
+let kExprRefCast = 0x41;
+let kExprBrOnCast = 0x42;
+
+// Numeric opcodes.
+let kExprI32SConvertSatF32 = 0x00;
+let kExprI32UConvertSatF32 = 0x01;
+let kExprI32SConvertSatF64 = 0x02;
+let kExprI32UConvertSatF64 = 0x03;
+let kExprI64SConvertSatF32 = 0x04;
+let kExprI64UConvertSatF32 = 0x05;
+let kExprI64SConvertSatF64 = 0x06;
+let kExprI64UConvertSatF64 = 0x07;
+let kExprMemoryInit = 0x08;
+let kExprDataDrop = 0x09;
+let kExprMemoryCopy = 0x0a;
+let kExprMemoryFill = 0x0b;
+let kExprTableInit = 0x0c;
+let kExprElemDrop = 0x0d;
+let kExprTableCopy = 0x0e;
+let kExprTableGrow = 0x0f;
+let kExprTableSize = 0x10;
+let kExprTableFill = 0x11;
+
+// Atomic opcodes.
+let kExprAtomicNotify = 0x00;
+let kExprI32AtomicWait = 0x01;
+let kExprI64AtomicWait = 0x02;
+let kExprI32AtomicLoad = 0x10;
+let kExprI32AtomicLoad8U = 0x12;
+let kExprI32AtomicLoad16U = 0x13;
+let kExprI32AtomicStore = 0x17;
+let kExprI32AtomicStore8U = 0x19;
+let kExprI32AtomicStore16U = 0x1a;
+let kExprI32AtomicAdd = 0x1e;
+let kExprI32AtomicAdd8U = 0x20;
+let kExprI32AtomicAdd16U = 0x21;
+let kExprI32AtomicSub = 0x25;
+let kExprI32AtomicSub8U = 0x27;
+let kExprI32AtomicSub16U = 0x28;
+let kExprI32AtomicAnd = 0x2c;
+let kExprI32AtomicAnd8U = 0x2e;
+let kExprI32AtomicAnd16U = 0x2f;
+let kExprI32AtomicOr = 0x33;
+let kExprI32AtomicOr8U = 0x35;
+let kExprI32AtomicOr16U = 0x36;
+let kExprI32AtomicXor = 0x3a;
+let kExprI32AtomicXor8U = 0x3c;
+let kExprI32AtomicXor16U = 0x3d;
+let kExprI32AtomicExchange = 0x41;
+let kExprI32AtomicExchange8U = 0x43;
+let kExprI32AtomicExchange16U = 0x44;
+let kExprI32AtomicCompareExchange = 0x48;
+let kExprI32AtomicCompareExchange8U = 0x4a;
+let kExprI32AtomicCompareExchange16U = 0x4b;
+
+let kExprI64AtomicLoad = 0x11;
+let kExprI64AtomicLoad8U = 0x14;
+let kExprI64AtomicLoad16U = 0x15;
+let kExprI64AtomicLoad32U = 0x16;
+let kExprI64AtomicStore = 0x18;
+let kExprI64AtomicStore8U = 0x1b;
+let kExprI64AtomicStore16U = 0x1c;
+let kExprI64AtomicStore32U = 0x1d;
+let kExprI64AtomicAdd = 0x1f;
+let kExprI64AtomicAdd8U = 0x22;
+let kExprI64AtomicAdd16U = 0x23;
+let kExprI64AtomicAdd32U = 0x24;
+let kExprI64AtomicSub = 0x26;
+let kExprI64AtomicSub8U = 0x29;
+let kExprI64AtomicSub16U = 0x2a;
+let kExprI64AtomicSub32U = 0x2b;
+let kExprI64AtomicAnd = 0x2d;
+let kExprI64AtomicAnd8U = 0x30;
+let kExprI64AtomicAnd16U = 0x31;
+let kExprI64AtomicAnd32U = 0x32;
+let kExprI64AtomicOr = 0x34;
+let kExprI64AtomicOr8U = 0x37;
+let kExprI64AtomicOr16U = 0x38;
+let kExprI64AtomicOr32U = 0x39;
+let kExprI64AtomicXor = 0x3b;
+let kExprI64AtomicXor8U = 0x3e;
+let kExprI64AtomicXor16U = 0x3f;
+let kExprI64AtomicXor32U = 0x40;
+let kExprI64AtomicExchange = 0x42;
+let kExprI64AtomicExchange8U = 0x45;
+let kExprI64AtomicExchange16U = 0x46;
+let kExprI64AtomicExchange32U = 0x47;
+let kExprI64AtomicCompareExchange = 0x49
+let kExprI64AtomicCompareExchange8U = 0x4c;
+let kExprI64AtomicCompareExchange16U = 0x4d;
+let kExprI64AtomicCompareExchange32U = 0x4e;
+
+// Simd opcodes.
+let kExprS128LoadMem = 0x00;
+let kExprS128Load8x8S = 0x01;
+let kExprS128Load8x8U = 0x02;
+let kExprS128Load16x4S = 0x03;
+let kExprS128Load16x4U = 0x04;
+let kExprS128Load32x2S = 0x05;
+let kExprS128Load32x2U = 0x06;
+let kExprS128Load8Splat = 0x07;
+let kExprS128Load16Splat = 0x08;
+let kExprS128Load32Splat = 0x09;
+let kExprS128Load64Splat = 0x0a;
+let kExprS128StoreMem = 0x0b;
+
+let kExprS128Const = 0x0c;
+let kExprI8x16Shuffle = 0x0d;
+
+let kExprI8x16Swizzle = 0x0e;
+let kExprI8x16Splat = 0x0f;
+let kExprI16x8Splat = 0x10;
+let kExprI32x4Splat = 0x11;
+let kExprI64x2Splat = 0x12;
+let kExprF32x4Splat = 0x13;
+let kExprF64x2Splat = 0x14;
+let kExprI8x16ReplaceLane = 0x17;
+let kExprI16x8ExtractLaneS = 0x18;
+let kExprI16x8ReplaceLane = 0x1a;
+let kExprI32x4ExtractLane = 0x1b;
+let kExprI32x4ReplaceLane = 0x1c;
+let kExprI64x2ReplaceLane = 0x1e;
+let kExprF32x4ReplaceLane = 0x20;
+let kExprF64x2ExtractLane = 0x21;
+let kExprF64x2ReplaceLane = 0x22;
+let kExprI8x16Eq = 0x23;
+let kExprI8x16Ne = 0x24;
+let kExprI8x16LtS = 0x25;
+let kExprI8x16LtU = 0x26;
+let kExprI8x16GtS = 0x27;
+let kExprI8x16GtU = 0x28;
+let kExprI8x16LeS = 0x29;
+let kExprI8x16LeU = 0x2a;
+let kExprI8x16GeS = 0x2b;
+let kExprI8x16GeU = 0x2c;
+let kExprI16x8Eq = 0x2d;
+let kExprI16x8Ne = 0x2e;
+let kExprI16x8LtS = 0x2f;
+let kExprI16x8LtU = 0x30;
+let kExprI16x8GtS = 0x31;
+let kExprI16x8GtU = 0x32;
+let kExprI16x8LeS = 0x33;
+let kExprI16x8LeU = 0x34;
+let kExprI16x8GeS = 0x35;
+let kExprI16x8GeU = 0x36;
+let kExprI32x4Eq = 0x37;
+let kExprI32x4Ne = 0x38;
+let kExprI32x4LtS = 0x39;
+let kExprI32x4LtU = 0x3a;
+let kExprI32x4GtS = 0x3b;
+let kExprI32x4GtU = 0x3c;
+let kExprI32x4LeS = 0x3d;
+let kExprI32x4LeU = 0x3e;
+let kExprI32x4GeS = 0x3f;
+let kExprI32x4GeU = 0x40;
+let kExprF32x4Eq = 0x41;
+let kExprF32x4Ne = 0x42;
+let kExprF32x4Lt = 0x43;
+let kExprF32x4Gt = 0x44;
+let kExprF32x4Le = 0x45;
+let kExprF32x4Ge = 0x46;
+let kExprF64x2Eq = 0x47;
+let kExprF64x2Ne = 0x48;
+let kExprF64x2Lt = 0x49;
+let kExprF64x2Gt = 0x4a;
+let kExprF64x2Le = 0x4b;
+let kExprF64x2Ge = 0x4c;
+let kExprS128Not = 0x4d;
+let kExprS128And = 0x4e;
+let kExprS128AndNot = 0x4f;
+let kExprS128Or = 0x50;
+let kExprS128Xor = 0x51;
+let kExprS128Select = 0x52;
+let kExprI8x16Abs = 0x60;
+let kExprI8x16Neg = 0x61;
+let kExprV128AnyTrue = 0x62;
+let kExprV8x16AllTrue = 0x63;
+let kExprI8x16SConvertI16x8 = 0x65;
+let kExprI8x16UConvertI16x8 = 0x66;
+let kExprI8x16Shl = 0x6b;
+let kExprI8x16ShrS = 0x6c;
+let kExprI8x16ShrU = 0x6d;
+let kExprI8x16Add = 0x6e;
+let kExprI8x16AddSatS = 0x6f;
+let kExprI8x16AddSatU = 0x70;
+let kExprI8x16Sub = 0x71;
+let kExprI8x16SubSatS = 0x72;
+let kExprI8x16SubSatU = 0x73;
+let kExprI8x16MinS = 0x76;
+let kExprI8x16MinU = 0x77;
+let kExprI8x16MaxS = 0x78;
+let kExprI8x16MaxU = 0x79;
+let kExprI8x16RoundingAverageU = 0x7b;
+let kExprI16x8Abs = 0x80;
+let kExprI16x8Neg = 0x81;
+let kExprV16x8AllTrue = 0x83;
+let kExprI16x8SConvertI32x4 = 0x85;
+let kExprI16x8UConvertI32x4 = 0x86;
+let kExprI16x8SConvertI8x16Low = 0x87;
+let kExprI16x8SConvertI8x16High = 0x88;
+let kExprI16x8UConvertI8x16Low = 0x89;
+let kExprI16x8UConvertI8x16High = 0x8a;
+let kExprI16x8Shl = 0x8b;
+let kExprI16x8ShrS = 0x8c;
+let kExprI16x8ShrU = 0x8d;
+let kExprI16x8Add = 0x8e;
+let kExprI16x8AddSatS = 0x8f;
+let kExprI16x8AddSatU = 0x90;
+let kExprI16x8Sub = 0x91;
+let kExprI16x8SubSatS = 0x92;
+let kExprI16x8SubSatU = 0x93;
+let kExprI16x8Mul = 0x95;
+let kExprI16x8MinS = 0x96;
+let kExprI16x8MinU = 0x97;
+let kExprI16x8MaxS = 0x98;
+let kExprI16x8MaxU = 0x99;
+let kExprI16x8RoundingAverageU = 0x9b;
+let kExprI32x4Abs = 0xa0;
+let kExprI32x4Neg = 0xa1;
+let kExprV32x4AllTrue = 0xa3;
+let kExprI32x4SConvertI16x8Low = 0xa7;
+let kExprI32x4SConvertI16x8High = 0xa8;
+let kExprI32x4UConvertI16x8Low = 0xa9;
+let kExprI32x4UConvertI16x8High = 0xaa;
+let kExprI32x4Shl = 0xab;
+let kExprI32x4ShrS = 0xac;
+let kExprI32x4ShrU = 0xad;
+let kExprI32x4Add = 0xae;
+let kExprI32x4Sub = 0xb1;
+let kExprI32x4Mul = 0xb5;
+let kExprI32x4MinS = 0xb6;
+let kExprI32x4MinU = 0xb7;
+let kExprI32x4MaxS = 0xb8;
+let kExprI32x4MaxU = 0xb9;
+let kExprI64x2Neg = 0xc1;
+let kExprI64x2Shl = 0xcb;
+let kExprI64x2ShrS = 0xcc;
+let kExprI64x2ShrU = 0xcd;
+let kExprI64x2Add = 0xce;
+let kExprI64x2Sub = 0xd1;
+let kExprI64x2Mul = 0xd5;
+let kExprI64x2ExtMulHighI32x4U = 0xd7;
+let kExprF32x4Abs = 0xe0;
+let kExprF32x4Neg = 0xe1;
+let kExprF32x4Sqrt = 0xe3;
+let kExprF32x4Add = 0xe4;
+let kExprF32x4Sub = 0xe5;
+let kExprF32x4Mul = 0xe6;
+let kExprF32x4Div = 0xe7;
+let kExprF32x4Min = 0xe8;
+let kExprF32x4Max = 0xe9;
+let kExprF64x2Abs = 0xec;
+let kExprF64x2Neg = 0xed;
+let kExprF64x2Sqrt = 0xef;
+let kExprF64x2Add = 0xf0;
+let kExprF64x2Sub = 0xf1;
+let kExprF64x2Mul = 0xf2;
+let kExprF64x2Div = 0xf3;
+let kExprF64x2Min = 0xf4;
+let kExprF64x2Max = 0xf5;
+let kExprI32x4SConvertF32x4 = 0xf8;
+let kExprI32x4UConvertF32x4 = 0xf9;
+let kExprF32x4SConvertI32x4 = 0xfa;
+let kExprF32x4UConvertI32x4 = 0xfb;
+
+// Compilation hint constants.
+let kCompilationHintStrategyDefault = 0x00;
+let kCompilationHintStrategyLazy = 0x01;
+let kCompilationHintStrategyEager = 0x02;
+let kCompilationHintStrategyLazyBaselineEagerTopTier = 0x03;
+let kCompilationHintTierDefault = 0x00;
+let kCompilationHintTierBaseline = 0x01;
+let kCompilationHintTierOptimized = 0x02;
+
+let kTrapUnreachable          = 0;
+let kTrapMemOutOfBounds       = 1;
+let kTrapDivByZero            = 2;
+let kTrapDivUnrepresentable   = 3;
+let kTrapRemByZero            = 4;
+let kTrapFloatUnrepresentable = 5;
+let kTrapTableOutOfBounds     = 6;
+let kTrapFuncSigMismatch      = 7;
+let kTrapUnalignedAccess      = 8;
+let kTrapDataSegmentDropped   = 9;
+let kTrapElemSegmentDropped   = 10;
+let kTrapRethrowNull          = 11;
+
+let kTrapMsgs = [
+  "unreachable",
+  "memory access out of bounds",
+  "divide by zero",
+  "divide result unrepresentable",
+  "remainder by zero",
+  "float unrepresentable in integer range",
+  "table index is out of bounds",
+  "function signature mismatch",
+  "operation does not support unaligned accesses",
+  "data segment has been dropped",
+  "element segment has been dropped",
+  "rethrowing null value"
+];
+
+function assertTraps(trap, code) {
+  assertThrows(code, WebAssembly.RuntimeError, kTrapMsgs[trap]);
+}
+
+class Binary {
+  constructor() {
+    this.length = 0;
+    this.buffer = new Uint8Array(8192);
+  }
+
+  ensure_space(needed) {
+    if (this.buffer.length - this.length >= needed) return;
+    let new_capacity = this.buffer.length * 2;
+    while (new_capacity - this.length < needed) new_capacity *= 2;
+    let new_buffer = new Uint8Array(new_capacity);
+    new_buffer.set(this.buffer);
+    this.buffer = new_buffer;
+  }
+
+  trunc_buffer() {
+    return new Uint8Array(this.buffer.buffer, 0, this.length);
+  }
+
+  reset() {
+    this.length = 0;
+  }
+
   emit_u8(val) {
-    this.push(val);
+    this.ensure_space(1);
+    this.buffer[this.length++] = val;
   }
 
   emit_u16(val) {
-    this.push(val & 0xff);
-    this.push((val >> 8) & 0xff);
+    this.ensure_space(2);
+    this.buffer[this.length++] = val;
+    this.buffer[this.length++] = val >> 8;
   }
 
   emit_u32(val) {
-    this.push(val & 0xff);
-    this.push((val >> 8) & 0xff);
-    this.push((val >> 16) & 0xff);
-    this.push((val >> 24) & 0xff);
+    this.ensure_space(4);
+    this.buffer[this.length++] = val;
+    this.buffer[this.length++] = val >> 8;
+    this.buffer[this.length++] = val >> 16;
+    this.buffer[this.length++] = val >> 24;
   }
 
-  emit_u32v(val) {
-    while (true) {
+  emit_leb_u(val, max_len) {
+    this.ensure_space(max_len);
+    for (let i = 0; i < max_len; ++i) {
       let v = val & 0xff;
       val = val >>> 7;
       if (val == 0) {
-        this.push(v);
-        break;
+        this.buffer[this.length++] = v;
+        return;
       }
-      this.push(v | 0x80);
+      this.buffer[this.length++] = v | 0x80;
     }
+    throw new Error("Leb value exceeds maximum length of " + max_len);
+  }
+
+  emit_u32v(val) {
+    this.emit_leb_u(val, kMaxVarInt32Size);
+  }
+
+  emit_u64v(val) {
+    this.emit_leb_u(val, kMaxVarInt64Size);
   }
 
   emit_bytes(data) {
-    for (let i = 0; i < data.length; i++) {
-      this.push(data[i] & 0xff);
-    }
+    this.ensure_space(data.length);
+    this.buffer.set(data, this.length);
+    this.length += data.length;
   }
 
   emit_string(string) {
@@ -60,37 +841,52 @@
     }
   }
 
+  emit_type(type) {
+    if ((typeof type) == "number") this.emit_u8(type);
+    else {
+      this.emit_u8(type.opcode);
+      if ('depth' in type) this.emit_u8(type.depth);
+      this.emit_u32v(type.index);
+    }
+  }
+
   emit_header() {
-    this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3,
-              kWasmV0, kWasmV1, kWasmV2, kWasmV3);
+    this.emit_bytes([
+      kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3
+    ]);
   }
 
   emit_section(section_code, content_generator) {
     // Emit section name.
     this.emit_u8(section_code);
     // Emit the section to a temporary buffer: its full length isn't know yet.
-    let section = new Binary;
+    const section = new Binary;
     content_generator(section);
     // Emit section length.
     this.emit_u32v(section.length);
     // Copy the temporary buffer.
-    for (var i = 0; i < section.length; i++) this.push(section[i]);
+    // Avoid spread because {section} can be huge.
+    this.emit_bytes(section.trunc_buffer());
   }
 }
 
 class WasmFunctionBuilder {
-  constructor(module, name, type_index) {
+  // Encoding of local names: a string corresponds to a local name,
+  // a number n corresponds to n undefined names.
+  constructor(module, name, type_index, arg_names) {
     this.module = module;
     this.name = name;
     this.type_index = type_index;
     this.body = [];
+    this.locals = [];
+    this.local_names = arg_names;
+    this.body_offset = undefined;  // Not valid until module is serialized.
   }
 
   numLocalNames() {
-    if (this.local_names === undefined) return 0;
     let num_local_names = 0;
     for (let loc_name of this.local_names) {
-      if (loc_name !== undefined) ++num_local_names;
+      if (typeof loc_name == "string") ++num_local_names;
     }
     return num_local_names;
   }
@@ -105,6 +901,11 @@
     return this;
   }
 
+  setCompilationHint(strategy, baselineTier, topTier) {
+    this.module.setCompilationHint(strategy, baselineTier, topTier, this.index);
+    return this;
+  }
+
   addBody(body) {
     for (let b of body) {
       if (typeof b !== 'number' || (b & (~0xFF)) !== 0 )
@@ -123,23 +924,18 @@
 
   getNumLocals() {
     let total_locals = 0;
-    for (let l of this.locals || []) {
-      for (let type of ["i32", "i64", "f32", "f64", "s128"]) {
-        total_locals += l[type + "_count"] || 0;
-      }
+    for (let l of this.locals) {
+      total_locals += l.count
     }
     return total_locals;
   }
 
-  addLocals(locals, names) {
-    const old_num_locals = this.getNumLocals();
-    if (!this.locals) this.locals = []
-    this.locals.push(locals);
-    if (names) {
-      if (!this.local_names) this.local_names = [];
-      const missing_names = old_num_locals - this.local_names.length;
-      this.local_names.push(...new Array(missing_names), ...names);
-    }
+  addLocals(type, count, names) {
+    this.locals.push({type: type, count: count});
+    names = names || [];
+    if (names.length > count) throw new Error('too many locals names given');
+    this.local_names.push(...names);
+    if (count > names.length) this.local_names.push(count - names.length);
     return this;
   }
 
@@ -163,22 +959,58 @@
   }
 }
 
+class WasmTableBuilder {
+  constructor(module, type, initial_size, max_size) {
+    this.module = module;
+    this.type = type;
+    this.initial_size = initial_size;
+    this.has_max = max_size != undefined;
+    this.max_size = max_size;
+  }
+
+  exportAs(name) {
+    this.module.exports.push({name: name, kind: kExternalTable,
+                              index: this.index});
+    return this;
+  }
+}
+
+function makeField(type, mutability) {
+  assertEquals("boolean", typeof mutability,
+               "field mutability must be boolean");
+  return {type: type, mutability: mutability};
+}
+
+class WasmStruct {
+  constructor(fields) {
+    assertTrue(Array.isArray(fields), "struct fields must be an array");
+    this.fields = fields;
+  }
+}
+
+class WasmArray {
+  constructor(type) {
+    this.type = type;
+  }
+}
+
 class WasmModuleBuilder {
   constructor() {
     this.types = [];
     this.imports = [];
     this.exports = [];
     this.globals = [];
+    this.tables = [];
     this.exceptions = [];
     this.functions = [];
-    this.function_table = [];
-    this.function_table_length_min = 0;
-    this.function_table_length_max = 0;
-    this.function_table_inits = [];
-    this.segments = [];
+    this.compilation_hints = [];
+    this.element_segments = [];
+    this.data_segments = [];
     this.explicit = [];
     this.num_imported_funcs = 0;
     this.num_imported_globals = 0;
+    this.num_imported_tables = 0;
+    this.num_imported_exceptions = 0;
     return this;
   }
 
@@ -187,8 +1019,25 @@
     return this;
   }
 
-  addMemory(min, max, exp, shared) {
-    this.memory = {min: min, max: max, exp: exp, shared: shared};
+  addMemory(min, max, exported, shared) {
+    this.memory = {
+      min: min,
+      max: max,
+      exported: exported,
+      shared: shared || false,
+      is_memory64: false
+    };
+    return this;
+  }
+
+  addMemory64(min, max, exported) {
+    this.memory = {
+      min: min,
+      max: max,
+      exported: exported,
+      shared: false,
+      is_memory64: true
+    };
     return this;
   }
 
@@ -203,14 +1052,21 @@
     for (var i = 0; i < name.length; i++) {
       result.emit_u8(name.charCodeAt(i));
     }
-    return result;
+    return result.trunc_buffer()
+  }
+
+  createCustomSection(name, bytes) {
+    name = this.stringToBytes(name);
+    var section = new Binary();
+    section.emit_u8(0);
+    section.emit_u32v(name.length + bytes.length);
+    section.emit_bytes(name);
+    section.emit_bytes(bytes);
+    return section.trunc_buffer();
   }
 
   addCustomSection(name, bytes) {
-    name = this.stringToBytes(name);
-    var length = new Binary();
-    length.emit_u32v(name.length + bytes.length);
-    this.explicit.push([0, ...length, ...name, ...bytes]);
+    this.explicit.push(this.createCustomSection(name, bytes));
   }
 
   addType(type) {
@@ -220,53 +1076,99 @@
     return this.types.length - 1;
   }
 
-  addGlobal(local_type, mutable) {
-    let glob = new WasmGlobalBuilder(this, local_type, mutable);
+  addStruct(fields) {
+    this.types.push(new WasmStruct(fields));
+    return this.types.length - 1;
+  }
+
+  addArray(type) {
+    this.types.push(new WasmArray(type));
+    return this.types.length - 1;
+  }
+
+  addGlobal(type, mutable) {
+    let glob = new WasmGlobalBuilder(this, type, mutable);
     glob.index = this.globals.length + this.num_imported_globals;
     this.globals.push(glob);
     return glob;
   }
 
-  addException(type) {
-    if (type.results.length != 0)
-      throw new Error('Invalid exception signature: ' + type);
-    this.exceptions.push(type);
-    return this.exceptions.length - 1;
+  addTable(type, initial_size, max_size = undefined) {
+    if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 ||
+        type == kWasmF64 || type == kWasmS128 || type == kWasmStmt) {
+      throw new Error('Tables must be of a reference type');
+    }
+    let table = new WasmTableBuilder(this, type, initial_size, max_size);
+    table.index = this.tables.length + this.num_imported_tables;
+    this.tables.push(table);
+    return table;
   }
 
-  addFunction(name, type) {
+  addException(type) {
     let type_index = (typeof type) == "number" ? type : this.addType(type);
-    let func = new WasmFunctionBuilder(this, name, type_index);
+    let except_index = this.exceptions.length + this.num_imported_exceptions;
+    this.exceptions.push(type_index);
+    return except_index;
+  }
+
+  addFunction(name, type, arg_names) {
+    arg_names = arg_names || [];
+    let type_index = (typeof type) == "number" ? type : this.addType(type);
+    let num_args = this.types[type_index].params.length;
+    if (num_args < arg_names.length) throw new Error("too many arg names provided");
+    if (num_args > arg_names.length) arg_names.push(num_args - arg_names.length);
+    let func = new WasmFunctionBuilder(this, name, type_index, arg_names);
     func.index = this.functions.length + this.num_imported_funcs;
     this.functions.push(func);
     return func;
   }
 
-  addImport(module = "", name, type) {
+  addImport(module, name, type) {
+    if (this.functions.length != 0) {
+      throw new Error('Imported functions must be declared before local ones');
+    }
     let type_index = (typeof type) == "number" ? type : this.addType(type);
     this.imports.push({module: module, name: name, kind: kExternalFunction,
-                       type: type_index});
+                       type_index: type_index});
     return this.num_imported_funcs++;
   }
 
-  addImportedGlobal(module = "", name, type, mutable = false) {
+  addImportedGlobal(module, name, type, mutable = false) {
+    if (this.globals.length != 0) {
+      throw new Error('Imported globals must be declared before local ones');
+    }
     let o = {module: module, name: name, kind: kExternalGlobal, type: type,
              mutable: mutable};
     this.imports.push(o);
     return this.num_imported_globals++;
   }
 
-  addImportedMemory(module = "", name, initial = 0, maximum, shared) {
+  addImportedMemory(module, name, initial = 0, maximum, shared) {
     let o = {module: module, name: name, kind: kExternalMemory,
              initial: initial, maximum: maximum, shared: shared};
     this.imports.push(o);
     return this;
   }
 
-  addImportedTable(module = "", name, initial, maximum) {
+  addImportedTable(module, name, initial, maximum, type) {
+    if (this.tables.length != 0) {
+      throw new Error('Imported tables must be declared before local ones');
+    }
     let o = {module: module, name: name, kind: kExternalTable, initial: initial,
-             maximum: maximum};
+             maximum: maximum, type: type || kWasmFuncRef};
     this.imports.push(o);
+    return this.num_imported_tables++;
+  }
+
+  addImportedException(module, name, type) {
+    if (this.exceptions.length != 0) {
+      throw new Error('Imported exceptions must be declared before local ones');
+    }
+    let type_index = (typeof type) == "number" ? type : this.addType(type);
+    let o = {module: module, name: name, kind: kExternalException,
+             type_index: type_index};
+    this.imports.push(o);
+    return this.num_imported_exceptions++;
   }
 
   addExport(name, index) {
@@ -275,31 +1177,60 @@
   }
 
   addExportOfKind(name, kind, index) {
+    if (index == undefined && kind != kExternalTable &&
+        kind != kExternalMemory) {
+      throw new Error(
+        'Index for exports other than tables/memories must be provided');
+    }
+    if (index !== undefined && (typeof index) != 'number') {
+      throw new Error('Index for exports must be a number')
+    }
     this.exports.push({name: name, kind: kind, index: index});
     return this;
   }
 
+  setCompilationHint(strategy, baselineTier, topTier, index) {
+    this.compilation_hints[index] = {strategy: strategy, baselineTier:
+      baselineTier, topTier: topTier};
+    return this;
+  }
+
   addDataSegment(addr, data, is_global = false) {
-    this.segments.push({addr: addr, data: data, is_global: is_global});
-    return this.segments.length - 1;
+    this.data_segments.push(
+        {addr: addr, data: data, is_global: is_global, is_active: true});
+    return this.data_segments.length - 1;
+  }
+
+  addPassiveDataSegment(data) {
+    this.data_segments.push({data: data, is_active: false});
+    return this.data_segments.length - 1;
   }
 
   exportMemoryAs(name) {
     this.exports.push({name: name, kind: kExternalMemory, index: 0});
   }
 
-  addFunctionTableInit(base, is_global, array, is_import = false) {
-    this.function_table_inits.push({base: base, is_global: is_global,
-                                    array: array});
-    if (!is_global) {
-      var length = base + array.length;
-      if (length > this.function_table_length_min && !is_import) {
-        this.function_table_length_min = length;
-      }
-      if (length > this.function_table_length_max && !is_import) {
-         this.function_table_length_max = length;
-      }
-    }
+  addElementSegment(table, base, is_global, array) {
+    this.element_segments.push({
+      table: table,
+      base: base,
+      is_global: is_global,
+      array: array,
+      is_active: true,
+      is_declarative: false
+    });
+    return this;
+  }
+
+  addPassiveElementSegment(array) {
+    this.element_segments.push(
+        {array: array, is_active: false, is_declarative: false});
+    return this;
+  }
+
+  addDeclarativeElementSegment(array) {
+    this.element_segments.push(
+        {array: array, is_active: false, is_declarative: true});
     return this;
   }
 
@@ -308,12 +1239,25 @@
       if (typeof n != 'number')
         throw new Error('invalid table (entries have to be numbers): ' + array);
     }
-    return this.addFunctionTableInit(this.function_table.length, false, array);
+    if (this.tables.length == 0) {
+      this.addTable(kWasmAnyFunc, 0);
+    }
+    // Adjust the table to the correct size.
+    let table = this.tables[0];
+    const base = table.initial_size;
+    const table_size = base + array.length;
+    table.initial_size = table_size;
+    if (table.has_max && table_size > table.max_size) {
+      table.max_size = table_size;
+    }
+    return this.addElementSegment(0, base, false, array);
   }
 
-  setFunctionTableBounds(min, max) {
-    this.function_table_length_min = min;
-    this.function_table_length_max = max;
+  setTableBounds(min, max = undefined) {
+    if (this.tables.length != 0) {
+      throw new Error("The table bounds of table '0' have already been set.");
+    }
+    this.addTable(kWasmAnyFunc, min, max);
     return this;
   }
 
@@ -322,7 +1266,7 @@
     return this;
   }
 
-  toArray(debug = false) {
+  toBuffer(debug = false) {
     let binary = new Binary;
     let wasm = this;
 
@@ -335,14 +1279,27 @@
       binary.emit_section(kTypeSectionCode, section => {
         section.emit_u32v(wasm.types.length);
         for (let type of wasm.types) {
-          section.emit_u8(kWasmFunctionTypeForm);
-          section.emit_u32v(type.params.length);
-          for (let param of type.params) {
-            section.emit_u8(param);
-          }
-          section.emit_u32v(type.results.length);
-          for (let result of type.results) {
-            section.emit_u8(result);
+          if (type instanceof WasmStruct) {
+            section.emit_u8(kWasmStructTypeForm);
+            section.emit_u32v(type.fields.length);
+            for (let field of type.fields) {
+              section.emit_type(field.type);
+              section.emit_u8(field.mutability ? 1 : 0);
+            }
+          } else if (type instanceof WasmArray) {
+            section.emit_u8(kWasmArrayTypeForm);
+            section.emit_type(type.type);
+            section.emit_u8(1); // Only mutable arrays supported currently.
+          } else {
+            section.emit_u8(kWasmFunctionTypeForm);
+            section.emit_u32v(type.params.length);
+            for (let param of type.params) {
+              section.emit_type(param);
+            }
+            section.emit_u32v(type.results.length);
+            for (let result of type.results) {
+              section.emit_type(result);
+            }
           }
         }
       });
@@ -358,9 +1315,9 @@
           section.emit_string(imp.name || '');
           section.emit_u8(imp.kind);
           if (imp.kind == kExternalFunction) {
-            section.emit_u32v(imp.type);
+            section.emit_u32v(imp.type_index);
           } else if (imp.kind == kExternalGlobal) {
-            section.emit_u32v(imp.type);
+            section.emit_type(imp.type);
             section.emit_u8(imp.mutable);
           } else if (imp.kind == kExternalMemory) {
             var has_max = (typeof imp.maximum) != "undefined";
@@ -373,11 +1330,14 @@
             section.emit_u32v(imp.initial); // initial
             if (has_max) section.emit_u32v(imp.maximum); // maximum
           } else if (imp.kind == kExternalTable) {
-            section.emit_u8(kWasmAnyFunctionTypeForm);
+            section.emit_type(imp.type);
             var has_max = (typeof imp.maximum) != "undefined";
             section.emit_u8(has_max ? 1 : 0); // flags
             section.emit_u32v(imp.initial); // initial
             if (has_max) section.emit_u32v(imp.maximum); // maximum
+          } else if (imp.kind == kExternalException) {
+            section.emit_u32v(kExceptionAttribute);
+            section.emit_u32v(imp.type_index);
           } else {
             throw new Error("unknown/unsupported import kind " + imp.kind);
           }
@@ -396,17 +1356,17 @@
       });
     }
 
-    // Add function_table.
-    if (wasm.function_table_length_min > 0) {
-      if (debug) print("emitting table @ " + binary.length);
+    // Add table section
+    if (wasm.tables.length > 0) {
+      if (debug) print ("emitting tables @ " + binary.length);
       binary.emit_section(kTableSectionCode, section => {
-        section.emit_u8(1);  // one table entry
-        section.emit_u8(kWasmAnyFunctionTypeForm);
-        // TODO(gdeepti): Cleanup to use optional max flag,
-        // fix up tests to set correct initial/maximum values
-        section.emit_u32v(1);
-        section.emit_u32v(wasm.function_table_length_min);
-        section.emit_u32v(wasm.function_table_length_max);
+        section.emit_u32v(wasm.tables.length);
+        for (let table of wasm.tables) {
+          section.emit_type(table.type);
+          section.emit_u8(table.has_max);
+          section.emit_u32v(table.initial_size);
+          if (table.has_max) section.emit_u32v(table.max_size);
+        }
       });
     }
 
@@ -416,15 +1376,34 @@
       binary.emit_section(kMemorySectionCode, section => {
         section.emit_u8(1);  // one memory entry
         const has_max = wasm.memory.max !== undefined;
-        const is_shared = wasm.memory.shared !== undefined;
-        // Emit flags (bit 0: reszeable max, bit 1: shared memory)
-        if (is_shared) {
-          section.emit_u8(has_max ? 3 : 2);
+        if (wasm.memory.is_memory64) {
+          assertFalse(
+              wasm.memory.shared, 'sharing memory64 is not supported (yet)');
+          section.emit_u8(
+              has_max ? kLimitsMemory64WithMaximum : kLimitsMemory64NoMaximum);
+          section.emit_u64v(wasm.memory.min);
+          if (has_max) section.emit_u64v(wasm.memory.max);
         } else {
-          section.emit_u8(has_max ? 1 : 0);
+          section.emit_u8(
+              wasm.memory.shared ?
+                  (has_max ? kLimitsSharedWithMaximum :
+                             kLimitsSharedNoMaximum) :
+                  (has_max ? kLimitsWithMaximum : kLimitsNoMaximum));
+          section.emit_u32v(wasm.memory.min);
+          if (has_max) section.emit_u32v(wasm.memory.max);
         }
-        section.emit_u32v(wasm.memory.min);
-        if (has_max) section.emit_u32v(wasm.memory.max);
+      });
+    }
+
+    // Add event section.
+    if (wasm.exceptions.length > 0) {
+      if (debug) print("emitting events @ " + binary.length);
+      binary.emit_section(kExceptionSectionCode, section => {
+        section.emit_u32v(wasm.exceptions.length);
+        for (let type_index of wasm.exceptions) {
+          section.emit_u32v(kExceptionAttribute);
+          section.emit_u32v(type_index);
+        }
       });
     }
 
@@ -434,7 +1413,7 @@
       binary.emit_section(kGlobalSectionCode, section => {
         section.emit_u32v(wasm.globals.length);
         for (let global of wasm.globals) {
-          section.emit_u8(global.type);
+          section.emit_type(global.type);
           section.emit_u8(global.mutable);
           if ((typeof global.init_index) == "undefined") {
             // Emit a constant initializer.
@@ -445,32 +1424,44 @@
               break;
             case kWasmI64:
               section.emit_u8(kExprI64Const);
-              section.emit_u32v(global.init);
+              section.emit_u64v(global.init);
               break;
             case kWasmF32:
-              section.emit_u8(kExprF32Const);
-              f32_view[0] = global.init;
-              section.emit_u8(byte_view[0]);
-              section.emit_u8(byte_view[1]);
-              section.emit_u8(byte_view[2]);
-              section.emit_u8(byte_view[3]);
+              section.emit_bytes(wasmF32Const(global.init));
               break;
             case kWasmF64:
-              section.emit_u8(kExprF64Const);
-              f64_view[0] = global.init;
-              section.emit_u8(byte_view[0]);
-              section.emit_u8(byte_view[1]);
-              section.emit_u8(byte_view[2]);
-              section.emit_u8(byte_view[3]);
-              section.emit_u8(byte_view[4]);
-              section.emit_u8(byte_view[5]);
-              section.emit_u8(byte_view[6]);
-              section.emit_u8(byte_view[7]);
+              section.emit_bytes(wasmF64Const(global.init));
+              break;
+            case kWasmS128:
+              section.emit_bytes(wasmS128Const(global.init));
+              break;
+            case kWasmExternRef:
+              section.emit_u8(kExprRefNull);
+              section.emit_u8(kWasmExternRef);
+              assertEquals(global.function_index, undefined);
+              break;
+            case kWasmAnyFunc:
+              if (global.function_index !== undefined) {
+                section.emit_u8(kExprRefFunc);
+                section.emit_u32v(global.function_index);
+              } else {
+                section.emit_u8(kExprRefNull);
+                section.emit_u8(kWasmAnyFunc);
+              }
+              break;
+            default:
+              if (global.function_index !== undefined) {
+                section.emit_u8(kExprRefFunc);
+                section.emit_u32v(global.function_index);
+              } else {
+                section.emit_u8(kExprRefNull);
+                section.emit_u32v(global.type.index);
+              }
               break;
             }
           } else {
             // Emit a global-index initializer.
-            section.emit_u8(kExprGetGlobal);
+            section.emit_u8(kExprGlobalGet);
             section.emit_u32v(global.init_index);
           }
           section.emit_u8(kExprEnd);  // end of init expression
@@ -479,7 +1470,7 @@
     }
 
     // Add export table.
-    var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
+    var mem_export = (wasm.memory !== undefined && wasm.memory.exported);
     var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
     if (exports_count > 0) {
       if (debug) print("emitting exports @ " + binary.length);
@@ -506,102 +1497,160 @@
       });
     }
 
-    // Add table elements.
-    if (wasm.function_table_inits.length > 0) {
-      if (debug) print("emitting table @ " + binary.length);
+    // Add element segments
+    if (wasm.element_segments.length > 0) {
+      if (debug) print("emitting element segments @ " + binary.length);
       binary.emit_section(kElementSectionCode, section => {
-        var inits = wasm.function_table_inits;
+        var inits = wasm.element_segments;
         section.emit_u32v(inits.length);
 
         for (let init of inits) {
-          section.emit_u8(0); // table index
-          if (init.is_global) {
-            section.emit_u8(kExprGetGlobal);
+          if (init.is_active) {
+            // Active segment.
+            if (init.table == 0) {
+              section.emit_u32v(kActiveNoIndex);
+            } else {
+              section.emit_u32v(kActiveWithIndex);
+              section.emit_u32v(init.table);
+            }
+            if (init.is_global) {
+              section.emit_u8(kExprGlobalGet);
+            } else {
+              section.emit_u8(kExprI32Const);
+            }
+            section.emit_u32v(init.base);
+            section.emit_u8(kExprEnd);
+            if (init.table != 0) {
+              section.emit_u8(kExternalFunction);
+            }
+            section.emit_u32v(init.array.length);
+            for (let index of init.array) {
+              section.emit_u32v(index);
+            }
+          } else if (
+              init.is_declarative &&
+              init.array.every(index => index !== null)) {
+            section.emit_u8(kDeclarative);
+            section.emit_u8(kExternalFunction);
+            section.emit_u32v(init.array.length);
+            for (let index of init.array) {
+              section.emit_u32v(index);
+            }
           } else {
-            section.emit_u8(kExprI32Const);
-          }
-          section.emit_u32v(init.base);
-          section.emit_u8(kExprEnd);
-          section.emit_u32v(init.array.length);
-          for (let index of init.array) {
-            section.emit_u32v(index);
+            // Passive or declarative segment with elements.
+            section.emit_u8(
+                init.is_declarative ? kDeclarativeWithElements :
+                                      kPassiveWithElements);  // flags
+            section.emit_u8(kWasmAnyFunc);
+            section.emit_u32v(init.array.length);
+            for (let index of init.array) {
+              if (index === null) {
+                section.emit_u8(kExprRefNull);
+                section.emit_u8(kWasmAnyFunc);
+                section.emit_u8(kExprEnd);
+              } else {
+                section.emit_u8(kExprRefFunc);
+                section.emit_u32v(index);
+                section.emit_u8(kExprEnd);
+              }
+            }
           }
         }
       });
     }
 
-    // Add exceptions.
-    if (wasm.exceptions.length > 0) {
-      if (debug) print("emitting exceptions @ " + binary.length);
-      binary.emit_section(kExceptionSectionCode, section => {
-        section.emit_u32v(wasm.exceptions.length);
-        for (let type of wasm.exceptions) {
-          section.emit_u32v(type.params.length);
-          for (let param of type.params) {
-            section.emit_u8(param);
-          }
-        }
+    // If there are any passive data segments, add the DataCount section.
+    if (wasm.data_segments.some(seg => !seg.is_active)) {
+      binary.emit_section(kDataCountSectionCode, section => {
+        section.emit_u32v(wasm.data_segments.length);
       });
     }
 
+    // If there are compilation hints add a custom section 'compilationHints'
+    // after the function section and before the code section.
+    if (wasm.compilation_hints.length > 0) {
+      if (debug) print("emitting compilation hints @ " + binary.length);
+      // Build custom section payload.
+      let payloadBinary = new Binary();
+      let implicit_compilation_hints_count = wasm.functions.length;
+      payloadBinary.emit_u32v(implicit_compilation_hints_count);
+
+      // Defaults to the compiler's choice if no better hint was given (0x00).
+      let defaultHintByte = kCompilationHintStrategyDefault |
+          (kCompilationHintTierDefault << 2) |
+          (kCompilationHintTierDefault << 4);
+
+      // Emit hint byte for every function defined in this module.
+      for (let i = 0; i < implicit_compilation_hints_count; i++) {
+        let index = wasm.num_imported_funcs + i;
+        var hintByte;
+        if(index in wasm.compilation_hints) {
+          let hint = wasm.compilation_hints[index];
+          hintByte = hint.strategy | (hint.baselineTier << 2) |
+              (hint.topTier << 4);
+        } else{
+          hintByte = defaultHintByte;
+        }
+        payloadBinary.emit_u8(hintByte);
+      }
+
+      // Finalize as custom section.
+      let name = "compilationHints";
+      let bytes = this.createCustomSection(name, payloadBinary.trunc_buffer());
+      binary.emit_bytes(bytes);
+    }
+
     // Add function bodies.
     if (wasm.functions.length > 0) {
       // emit function bodies
       if (debug) print("emitting code @ " + binary.length);
+      let section_length = 0;
       binary.emit_section(kCodeSectionCode, section => {
         section.emit_u32v(wasm.functions.length);
+        let header = new Binary;
         for (let func of wasm.functions) {
+          header.reset();
           // Function body length will be patched later.
-          let local_decls = [];
-          for (let l of func.locals || []) {
-            if (l.i32_count > 0) {
-              local_decls.push({count: l.i32_count, type: kWasmI32});
-            }
-            if (l.i64_count > 0) {
-              local_decls.push({count: l.i64_count, type: kWasmI64});
-            }
-            if (l.f32_count > 0) {
-              local_decls.push({count: l.f32_count, type: kWasmF32});
-            }
-            if (l.f64_count > 0) {
-              local_decls.push({count: l.f64_count, type: kWasmF64});
-            }
-            if (l.s128_count > 0) {
-              local_decls.push({count: l.s128_count, type: kWasmS128});
-            }
-          }
-
-          let header = new Binary;
+          let local_decls = func.locals || [];
           header.emit_u32v(local_decls.length);
           for (let decl of local_decls) {
             header.emit_u32v(decl.count);
-            header.emit_u8(decl.type);
+            header.emit_type(decl.type);
           }
-
           section.emit_u32v(header.length + func.body.length);
-          section.emit_bytes(header);
+          section.emit_bytes(header.trunc_buffer());
+          // Set to section offset for now, will update.
+          func.body_offset = section.length;
           section.emit_bytes(func.body);
         }
+        section_length = section.length;
       });
+      for (let func of wasm.functions) {
+        func.body_offset += binary.length - section_length;
+      }
     }
 
     // Add data segments.
-    if (wasm.segments.length > 0) {
+    if (wasm.data_segments.length > 0) {
       if (debug) print("emitting data segments @ " + binary.length);
       binary.emit_section(kDataSectionCode, section => {
-        section.emit_u32v(wasm.segments.length);
-        for (let seg of wasm.segments) {
-          section.emit_u8(0);  // linear memory index 0
-          if (seg.is_global) {
-            // initializer is a global variable
-            section.emit_u8(kExprGetGlobal);
-            section.emit_u32v(seg.addr);
+        section.emit_u32v(wasm.data_segments.length);
+        for (let seg of wasm.data_segments) {
+          if (seg.is_active) {
+            section.emit_u8(0);  // linear memory index 0 / flags
+            if (seg.is_global) {
+              // initializer is a global variable
+              section.emit_u8(kExprGlobalGet);
+              section.emit_u32v(seg.addr);
+            } else {
+              // initializer is a constant
+              section.emit_u8(kExprI32Const);
+              section.emit_u32v(seg.addr);
+            }
+            section.emit_u8(kExprEnd);
           } else {
-            // initializer is a constant
-            section.emit_u8(kExprI32Const);
-            section.emit_u32v(seg.addr);
+            section.emit_u8(kPassive);  // flags
           }
-          section.emit_u8(kExprEnd);
           section.emit_u32v(seg.data.length);
           section.emit_bytes(seg.data);
         }
@@ -651,10 +1700,15 @@
               if (func.numLocalNames() == 0) continue;
               name_section.emit_u32v(func.index);
               name_section.emit_u32v(func.numLocalNames());
+              let name_index = 0;
               for (let i = 0; i < func.local_names.length; ++i) {
-                if (func.local_names[i] === undefined) continue;
-                name_section.emit_u32v(i);
-                name_section.emit_string(func.local_names[i]);
+                if (typeof func.local_names[i] == "string") {
+                  name_section.emit_u32v(name_index);
+                  name_section.emit_string(func.local_names[i]);
+                  name_index++;
+                } else {
+                  name_index += func.local_names[i];
+                }
               }
             }
           });
@@ -662,23 +1716,15 @@
       });
     }
 
-    return binary;
+    return binary.trunc_buffer();
   }
 
-  toBuffer(debug = false) {
-    let bytes = this.toArray(debug);
-    let buffer = new ArrayBuffer(bytes.length);
-    let view = new Uint8Array(buffer);
-    for (let i = 0; i < bytes.length; i++) {
-      let val = bytes[i];
-      if ((typeof val) == "string") val = val.charCodeAt(0);
-      view[i] = val | 0;
-    }
-    return buffer;
+  toArray(debug = false) {
+    return Array.from(this.toBuffer(debug));
   }
 
   instantiate(ffi) {
-    let module = new WebAssembly.Module(this.toBuffer());
+    let module = this.toModule();
     let instance = new WebAssembly.Instance(module, ffi);
     return instance;
   }
@@ -692,3 +1738,49 @@
     return new WebAssembly.Module(this.toBuffer(debug));
   }
 }
+
+function wasmSignedLeb(val, max_len = 5) {
+  let res = [];
+  for (let i = 0; i < max_len; ++i) {
+    let v = val & 0x7f;
+    // If {v} sign-extended from 7 to 32 bits is equal to val, we are done.
+    if (((v << 25) >> 25) == val) {
+      res.push(v);
+      return res;
+    }
+    res.push(v | 0x80);
+    val = val >> 7;
+  }
+  throw new Error(
+      'Leb value <' + val + '> exceeds maximum length of ' + max_len);
+}
+
+function wasmI32Const(val) {
+  return [kExprI32Const, ...wasmSignedLeb(val, 5)];
+}
+
+function wasmF32Const(f) {
+  // Write in little-endian order at offset 0.
+  data_view.setFloat32(0, f, true);
+  return [
+    kExprF32Const, byte_view[0], byte_view[1], byte_view[2], byte_view[3]
+  ];
+}
+
+function wasmF64Const(f) {
+  // Write in little-endian order at offset 0.
+  data_view.setFloat64(0, f, true);
+  return [
+    kExprF64Const, byte_view[0], byte_view[1], byte_view[2],
+    byte_view[3], byte_view[4], byte_view[5], byte_view[6], byte_view[7]
+  ];
+}
+
+function wasmS128Const(f) {
+  // Write in little-endian order at offset 0.
+  return [kSimdPrefix, kExprS128Const, ...f];
+}
+
+function getOpcodeName(opcode) {
+  return globalThis.kWasmOpcodeNames?.[opcode] ?? 'unknown';
+}
diff --git a/third_party/blink/web_tests/http/tests/wasm/wasm_worker_termination_while_compiling.html b/third_party/blink/web_tests/http/tests/wasm/wasm_worker_termination_while_compiling.html
index 8dd42d6..ca86f78 100644
--- a/third_party/blink/web_tests/http/tests/wasm/wasm_worker_termination_while_compiling.html
+++ b/third_party/blink/web_tests/http/tests/wasm/wasm_worker_termination_while_compiling.html
@@ -1,7 +1,6 @@
 <!DOCTYPE html>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<script src="resources/wasm-constants.js"></script>
 <script src="resources/wasm-module-builder.js"></script>
 <script>
 
diff --git a/third_party/blink/web_tests/http/tests/wasm_streaming/regression860637.html b/third_party/blink/web_tests/http/tests/wasm_streaming/regression860637.html
index b0c9722b..4a92c3e8 100644
--- a/third_party/blink/web_tests/http/tests/wasm_streaming/regression860637.html
+++ b/third_party/blink/web_tests/http/tests/wasm_streaming/regression860637.html
@@ -2,7 +2,6 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="wasm_response_apis.js"></script>
-<script src="../wasm/resources/wasm-constants.js"></script>
 <script src="../wasm/resources/wasm-module-builder.js"></script>
 <script>
   promise_test(TestRegression837417, "Regression test");
diff --git a/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.html b/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.html
index 6701745..bd16cc1d 100644
--- a/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.html
+++ b/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.html
@@ -2,7 +2,6 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="wasm_response_apis.js"></script>
-<script src="../wasm/resources/wasm-constants.js"></script>
 <script src="../wasm/resources/wasm-module-builder.js"></script>
 <script>
   promise_test(TestStreamedCompile, "test compileStreaming");
diff --git a/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.js b/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.js
index 316f37e..32d6ac6 100644
--- a/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.js
+++ b/third_party/blink/web_tests/http/tests/wasm_streaming/wasm_response_apis.js
@@ -156,11 +156,11 @@
 
   builder.addFunction("main", kSig_i_i)
     .addBody([
-      kExprGetLocal, 0,
+      kExprLocalGet, 0,
       kExprI32LoadMem, 0, 0,
       kExprI32Const, 1,
       kExprCallIndirect, signature, kTableZero,
-      kExprGetLocal,0,
+      kExprLocalGet,0,
       kExprI32LoadMem,0, 0,
       kExprCallFunction, 0,
       kExprI32Add
@@ -170,7 +170,7 @@
   // return mem[i] + some_value();
   builder.addFunction("_wrap_writer", signature)
     .addBody([
-      kExprGetLocal, 0,
+      kExprLocalGet, 0,
       kExprCallFunction, 1]);
   builder.appendToTable([2, 3]);
 
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
index a4a09724..91f25c2 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
index b5eb3d5..05f2896 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 26238bd..a47cc3cd 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index 54aa47f..90fc151 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index b8e6363d..2bd0151d 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
new file mode 100644
index 0000000..91f25c2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
new file mode 100644
index 0000000..05f2896
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png
new file mode 100644
index 0000000..b5353a78
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt
new file mode 100644
index 0000000..8910d66
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS Empty tables can still get a lsyout
+FAIL Empty tables do not take table-columns into account assert_equals: expected 50 but got 200
+FAIL Empty tables do not take table-rows into account assert_equals: expected 50 but got 100
+PASS Table-columns are taken into account after missing cells are generated (empty line)
+PASS Table-columns are taken into account after missing cells are generated (partially empty line)
+PASS Table-columns are taken into account after missing cells are generated (non-empty line)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-expected.png
similarity index 100%
copy from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
copy to third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-vertical-expected.png
similarity index 100%
copy from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
copy to third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png
new file mode 100644
index 0000000..b23aafb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
index 2c4cd186..0aacb4b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
index 0518096..aabc7f20a 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
index efa6ca86..44cde41 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index ed37f5f..bdbf39c 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index b23aafb..15d23402 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index 0d51927..bd778a6 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
index 6b8125f..d7b36f9 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
index 80e7e7e1..e1fe026 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png b/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
index c581137..2d6d7ae 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 374c7a5..7e7b317 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index 5e3dca2..3b7028f 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index 8018881..7341ad8 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/external/wpt/xhr/responsexml-document-properties-expected.txt b/third_party/blink/web_tests/platform/win7/external/wpt/xhr/responsexml-document-properties-expected.txt
deleted file mode 100644
index 63dbbc8c..0000000
--- a/third_party/blink/web_tests/platform/win7/external/wpt/xhr/responsexml-document-properties-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS domain
-PASS URL
-PASS documentURI
-PASS baseURI
-PASS referrer
-PASS title
-PASS contentType
-PASS readyState
-PASS location
-PASS defaultView
-PASS body
-PASS doctype
-PASS all
-PASS cookie
-PASS Test document URL properties after redirect
-PASS Test document URL properties of document with <base> after redirect
-FAIL lastModified set to time of response if no HTTP header provided assert_less_than_equal: expected a number less than or equal to 1566177285 but got 1566180885
-FAIL lastModified set to related HTTP header if provided assert_equals: expected 1566155231000 but got 1566151631000
-PASS cookie (after setting it)
-PASS styleSheets should be an object
-PASS implementation should be an object
-PASS images should be an object
-PASS forms should be an object
-PASS links should be an object
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/resources/testdriver-vendor.js b/third_party/blink/web_tests/resources/testdriver-vendor.js
index a3bebdd2..6177dd4 100644
--- a/third_party/blink/web_tests/resources/testdriver-vendor.js
+++ b/third_party/blink/web_tests/resources/testdriver-vendor.js
@@ -168,7 +168,7 @@
 
   window.test_driver_internal.action_sequence = function(actions) {
     if (window.top !== window) {
-      return Promise.reject(new Error("can only send keys in top-level window"));
+      return Promise.reject(new Error("can only send actions in top-level window"));
     }
 
     var didScrollIntoView = false;
@@ -177,6 +177,12 @@
       var last_y_position = 0;
       var first_pointer_down = false;
       for (let j = 0; j < actions[i].actions.length; j++) {
+        if (actions[i].actions[j].type == "keyDown" ||
+            actions[i].actions[j].type == "keyUp") {
+          return Promise.reject(new Error("we do not support keydown and keyup actions, " +
+                                          "please use test_driver.send_keys"));
+        }
+
         if ('origin' in actions[i].actions[j]) {
           if (typeof(actions[i].actions[j].origin) === 'string') {
              if (actions[i].actions[j].origin == "viewport") {
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index ea9a51e..7e6918f 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1030,6 +1030,7 @@
 [Worker]     method lineTo
 [Worker]     method measureText
 [Worker]     method moveTo
+[Worker]     method perspective
 [Worker]     method putImageData
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
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 44829e1..c220371 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
@@ -1044,6 +1044,7 @@
     method lineTo
     method measureText
     method moveTo
+    method perspective
     method putImageData
     method quadraticCurveTo
     method rect
@@ -5763,6 +5764,7 @@
     method lineTo
     method measureText
     method moveTo
+    method perspective
     method putImageData
     method quadraticCurveTo
     method rect
@@ -10562,7 +10564,7 @@
 interface XRDepthInformation
     attribute @@toStringTag
     getter height
-    getter normTextureFromNormView
+    getter normDepthBufferFromNormView
     getter rawValueToMeters
     getter width
     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 3b672a18..4dfe890 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -931,6 +931,7 @@
 [Worker]     method lineTo
 [Worker]     method measureText
 [Worker]     method moveTo
+[Worker]     method perspective
 [Worker]     method putImageData
 [Worker]     method quadraticCurveTo
 [Worker]     method rect
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html
deleted file mode 100644
index 2782315a..0000000
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_arguments.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!doctype html>
-<title>Scheduling API: Task Argument Passing</title>
-<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
-<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-'use strict';
-
-async_test(t => {
-  scheduler.postTask(t.step_func_done((arg1, arg2) => {
-    assert_equals(arg1, 'foo');
-    assert_equals(arg2, 10);
-  }), { priority: 'user-visible' }, 'foo', 10);
-}, 'Test scheduler.postTask correctly passes arguments');
-
-</script>
diff --git a/third_party/boringssl/BUILD.generated.gni b/third_party/boringssl/BUILD.generated.gni
index 740267a..aee38c2 100644
--- a/third_party/boringssl/BUILD.generated.gni
+++ b/third_party/boringssl/BUILD.generated.gni
@@ -49,6 +49,7 @@
   "src/crypto/bio/printf.c",
   "src/crypto/bio/socket.c",
   "src/crypto/bio/socket_helper.c",
+  "src/crypto/blake2/blake2.c",
   "src/crypto/bn_extra/bn_asn1.c",
   "src/crypto/bn_extra/convert.c",
   "src/crypto/buf/buf.c",
@@ -301,6 +302,7 @@
   "src/include/openssl/base.h",
   "src/include/openssl/base64.h",
   "src/include/openssl/bio.h",
+  "src/include/openssl/blake2.h",
   "src/include/openssl/blowfish.h",
   "src/include/openssl/bn.h",
   "src/include/openssl/buf.h",
diff --git a/third_party/boringssl/BUILD.generated_tests.gni b/third_party/boringssl/BUILD.generated_tests.gni
index d7a9f2a5..eb9ea3d 100644
--- a/third_party/boringssl/BUILD.generated_tests.gni
+++ b/third_party/boringssl/BUILD.generated_tests.gni
@@ -30,6 +30,7 @@
   "src/crypto/asn1/asn1_test.cc",
   "src/crypto/base64/base64_test.cc",
   "src/crypto/bio/bio_test.cc",
+  "src/crypto/blake2/blake2_test.cc",
   "src/crypto/buf/buf_test.cc",
   "src/crypto/bytestring/bytestring_test.cc",
   "src/crypto/chacha/chacha_test.cc",
@@ -93,6 +94,7 @@
 ]
 
 crypto_test_data = [
+  "src/crypto/blake2/blake2b256_tests.txt",
   "src/crypto/cipher_extra/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt",
   "src/crypto/cipher_extra/test/aes_128_cbc_sha1_tls_tests.txt",
   "src/crypto/cipher_extra/test/aes_128_cbc_sha256_tls_tests.txt",
diff --git a/third_party/chromevox/OWNERS b/third_party/chromevox/OWNERS
index dfab84a0..976b955 100644
--- a/third_party/chromevox/OWNERS
+++ b/third_party/chromevox/OWNERS
@@ -1,4 +1 @@
-aboxhall@chromium.org
-dmazzoni@chromium.org
-dtseng@chromium.org
-plundblad@chromium.org
+file://ui/accessibility/OWNERS
diff --git a/third_party/chromevox/third_party/sre/OWNERS b/third_party/chromevox/third_party/sre/OWNERS
deleted file mode 100644
index 3f3d042a..0000000
--- a/third_party/chromevox/third_party/sre/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-dtseng@chromium.org
-dmazzoni@chromium.org
diff --git a/third_party/chromevox/third_party/sre/README.md b/third_party/chromevox/third_party/sre/README.md
new file mode 100644
index 0000000..eda69ab
--- /dev/null
+++ b/third_party/chromevox/third_party/sre/README.md
@@ -0,0 +1,9 @@
+# Speech Rule Engine
+
+This project generates human-friendly readings appropriate for text-to-speech
+playback given Math ML.
+
+Chrome OS accessibility extensions e.g. ChromeVox, use it for this purpose.
+
+The project also contains localizations for a large number of unicode codepoints
+appropriate for tts reading.
diff --git a/third_party/subresource-filter-ruleset/README.chromium b/third_party/subresource-filter-ruleset/README.chromium
index d8943fab9..e1835f9d 100644
--- a/third_party/subresource-filter-ruleset/README.chromium
+++ b/third_party/subresource-filter-ruleset/README.chromium
@@ -1,6 +1,6 @@
 Name: EasyList
 URL: https://easylist.to/easylist/easylist.txt
-Version: 202010221614
+Version: 202101291627
 License: Creative Commons Attribution-ShareAlike 3.0 Unported
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1 b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
index 56419e6..ea65a83 100644
--- a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
+++ b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
@@ -1 +1 @@
-83bced6c2676ed8d7c57a84c9a8d4f76c08f79e2
\ No newline at end of file
+7dc8bceb0b3f31ff30276c543da7a62dcecd2299
\ No newline at end of file
diff --git a/third_party/tflite-support/README.chromium b/third_party/tflite-support/README.chromium
index 190a4e68..397fe52 100644
--- a/third_party/tflite-support/README.chromium
+++ b/third_party/tflite-support/README.chromium
@@ -23,6 +23,7 @@
 - Use _Exit instead of _exit to work on all platforms (0001-use-exit.patch)
 - Remove external file handlers support for memory mapping files to support Windows
   (0001-remove-unsupported-memory-map-from-file-handler.patch)
+- Fixes sign compare issues in tflite-support (0001-task-utils-sign-compare.patch)
 
 Third party dependencies:
 - tflite
diff --git a/third_party/tflite-support/patches/0001-task-utils-sign-compare.patch b/third_party/tflite-support/patches/0001-task-utils-sign-compare.patch
new file mode 100644
index 0000000..85172c99
--- /dev/null
+++ b/third_party/tflite-support/patches/0001-task-utils-sign-compare.patch
@@ -0,0 +1,34 @@
+From f84b50f175efff54ee6a6ef795703907245260cd Mon Sep 17 00:00:00 2001
+From: Sophie Chang <sophiechang@chromium.org>
+Date: Wed, 10 Feb 2021 17:55:30 +0000
+Subject: [PATCH] fix sign issues
+
+---
+ .../src/tensorflow_lite_support/cc/task/core/task_utils.h     | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h b/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
+index 744dbbfb0f80..ced3dbcae9e4 100644
+--- a/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
++++ b/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
+@@ -119,7 +119,7 @@ inline void PopulateVector(const TfLiteTensor* tensor, std::vector<T>* data) {
+   const T* results = GetTensorData<T>(tensor);
+   size_t num = tensor->bytes / sizeof(tensor->type);
+   data->reserve(num);
+-  for (int i = 0; i < num; i++) {
++  for (size_t i = 0; i < num; i++) {
+     data->emplace_back(results[i]);
+   }
+ }
+@@ -169,7 +169,7 @@ static TensorType* FindTensorByName(
+       tensor_metadatas->size() != tensors.size()) {
+     return nullptr;
+   }
+-  for (int i = 0; i < tensor_metadatas->size(); i++) {
++  for (flatbuffers::uoffset_t i = 0; i < tensor_metadatas->size(); i++) {
+     if (strcmp(name.data(), tensor_metadatas->Get(i)->name()->c_str()) == 0) {
+       return tensors[i];
+     }
+-- 
+2.30.0.478.g8a0d178c01-goog
+
diff --git a/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h b/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
index 744dbbf..ced3dbc 100644
--- a/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
+++ b/third_party/tflite-support/src/tensorflow_lite_support/cc/task/core/task_utils.h
@@ -119,7 +119,7 @@
   const T* results = GetTensorData<T>(tensor);
   size_t num = tensor->bytes / sizeof(tensor->type);
   data->reserve(num);
-  for (int i = 0; i < num; i++) {
+  for (size_t i = 0; i < num; i++) {
     data->emplace_back(results[i]);
   }
 }
@@ -169,7 +169,7 @@
       tensor_metadatas->size() != tensors.size()) {
     return nullptr;
   }
-  for (int i = 0; i < tensor_metadatas->size(); i++) {
+  for (flatbuffers::uoffset_t i = 0; i < tensor_metadatas->size(); i++) {
     if (strcmp(name.data(), tensor_metadatas->Get(i)->name()->c_str()) == 0) {
       return tensors[i];
     }
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
index 5461c61..731d12f 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
@@ -223,11 +223,13 @@
         };
 
         // clip space coordinates + texture space coordinates
+        // our depth buffer has an origin in top-left corner of the screen -
+        // we need to adjust the texture coordinates to account for that
         const vertices_data = [
-          -1, -1,    0, 0,  // bottom left
-           1, -1,    1, 0,  // bottom right
-          -1,  1,    0, 1,  // top left
-           1,  1,    1, 1,  // top right
+          -1, -1,    0, 1,  // bottom left
+           1, -1,    1, 1,  // bottom right
+          -1,  1,    0, 0,  // top left
+           1,  1,    1, 0,  // top right
         ];
 
         vertexBuffer = uploadVertexData(vertices_data);
@@ -366,7 +368,7 @@
         gl.uniform1i(programInfo.uniformLocations.depthTexture, 0);
 
         gl.uniformMatrix4fv(programInfo.uniformLocations.uvTransform, false,
-                            depthData.normTextureFromNormView.matrix);
+                            depthData.normDepthBufferFromNormView.matrix);
 
         gl.uniform1f(programInfo.uniformLocations.rawValueToMeters,
                      depthData.rawValueToMeters);
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
index a05c805..4d530ea 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
@@ -303,57 +303,129 @@
         }
       }
 
-      function renderDepthInformationCPU(depthData, view, viewport) {
+      function calculateVerticesFromDepth(depthData, viewport) {
+        // This function calculates vertices data going from depth buffer
+        // coordinates into normalized view coordinates to then fetch the
+        // distance using the XRCPUDepthInformation.getDepthInMeters() helper.
+        // The normalized view coordinates are then converted to NDC in order
+        // to produce the vertices data.
+
         const RESOLUTION = 5;
 
         const depth_width = depthData.width;
         const depth_height = depthData.height;
 
-        const norm_tex_from_norm_view = depthData.normTextureFromNormView.matrix;
-        const norm_view_from_norm_tex = mat4.invert(mat4.create(), norm_tex_from_norm_view);
+        const X_RANGE = depth_width;
+        const Y_RANGE = depth_height;
+
+        const vertices_data = [];
+
+        const norm_depth_from_norm_view = depthData.normDepthBufferFromNormView.matrix;
+        const norm_view_from_norm_depth = mat4.invert(mat4.create(), norm_depth_from_norm_view);
 
         const inverse_depth_dimensions = vec3.fromValues(1.0 / depth_width,
                                                          1.0 / depth_height,
                                                          0);
-        const viewport_dimensions = vec3.fromValues(viewport.width, viewport.height, 0);
 
-        const depth_vec = vec3.fromValues(depth_width-1, depth_height-1, 0);
-        const zeroes_vec = vec3.fromValues(0, 0, 0);
+        for(let x = 0; x < X_RANGE; x = x + RESOLUTION) {
+          for(let y = 0; y < Y_RANGE; y = y + RESOLUTION) {
 
-        const vertices_data = [];
+            // We start with absolute depth buffer coordinates:
+            const depth_coords_depth_buffer = vec3.fromValues(x, y, 0);
 
-        for(let x = 0; x < depth_width; x = x + RESOLUTION) {
-          for(let y = 0; y < depth_height; y = y + RESOLUTION) {
-            const distance = depthData.getDepthInMeters(x, y);
+            // Normalize them by depth buffer dimensions:
+            const depth_cooords_norm_depth_buffer = scaleByVec(vec3.create(),
+                                                               depth_coords_depth_buffer,
+                                                               inverse_depth_dimensions);
 
-            const depth_coords = vec3.fromValues(x, y, 0);
-
-            // Calculate the view coordinates that correspond to (x, y) depth coordiante:
-            // 1. Normalize to [0, 1] range:
-            const depth_coords_norm = scaleByVec(vec3.create(),
-                                                 depth_coords, inverse_depth_dimensions);
-            // 2. Map from normalized texture coordinates to normalized view coordinates:
+            // Transform to normalized view coordinates:
             const depth_coords_view_norm = vec3.transformMat4(vec3.create(),
-                                                              depth_coords_norm,
-                                                              norm_view_from_norm_tex);
-            // 3. Denormalize by viewport dimensions:
-            const depth_coords_view = scaleByVec(vec3.create(),
-                                                 depth_coords_view_norm, viewport_dimensions);
-            clamp(depth_coords_view, depth_coords_view, zeroes_vec, viewport_dimensions);
+                                                              depth_cooords_norm_depth_buffer,
+                                                              norm_view_from_norm_depth);
 
-            // Convert to NDC:
-            depth_coords_view[0] = (2.0 * depth_coords_view[0]) / viewport.width - 1;
-            depth_coords_view[1] = (2.0 * depth_coords_view[1]) / viewport.height - 1;
-
-            if(depth_coords_view[0] > 1 || depth_coords_view[0] < -1 ||
-               depth_coords_view[1] > 1 || depth_coords_view[1] < -1) {
+            if(depth_coords_view_norm[0] < 0 || depth_coords_view_norm[0] > 1 ||
+               depth_coords_view_norm[1] < 0 || depth_coords_view_norm[1] > 1) {
               continue;
             }
 
-            vertices_data.push(depth_coords_view[0], depth_coords_view[1], distance);
+            // getDepthInMeters() accepts inputs in normalized view coordinate system
+            // that has origin in top left corner, X's grow to the right, and Y's grow
+            // downward.
+            const distance = depthData.getDepthInMeters(depth_coords_view_norm[0],
+                                                        depth_coords_view_norm[1]);
+
+            // We need to convert normalized view coordinates to normalized device coordinates,
+            // with the origin at the center of a cube with side length = 2 and Y growing upward.
+            const depth_coords_ndc = vec3.clone(depth_coords_view_norm);
+
+            // First, fix up the Y axis:
+            depth_coords_ndc[1] = 1 - depth_coords_ndc[1];
+
+            // Then, convert to range [-1, 1]:
+            depth_coords_ndc[0] = (2.0 * depth_coords_ndc[0]) - 1;
+            depth_coords_ndc[1] = (2.0 * depth_coords_ndc[1]) - 1;
+
+            if(depth_coords_ndc[0] > 1 || depth_coords_ndc[0] < -1 ||
+               depth_coords_ndc[1] > 1 || depth_coords_ndc[1] < -1) {
+              continue;
+            }
+
+            vertices_data.push(depth_coords_ndc[0], depth_coords_ndc[1], distance);
           }
         }
 
+        return vertices_data;
+      }
+
+      function calculateVerticesFromViewCoordinates(depthData, viewport) {
+        // This function calculates vertices data going directly from normalized
+        // view coordinates & using the XRCPUDepthInformation.getDepthInMeters()
+        // helper. Normalized view coordinates are then converted to NDC.
+
+        const smaller_dim = Math.min(viewport.width, viewport.height);
+        const larger_dim = Math.max(viewport.width, viewport.height);
+        const is_portrait = smaller_dim == viewport.width;
+
+        const larger_dim_resolution = (smaller_dim * 0.1) / larger_dim;
+
+        const X_RESOLUTION = is_portrait ? 0.1 : larger_dim_resolution;
+        const Y_RESOLUTION = is_portrait ? larger_dim_resolution : 0.1;
+
+        const X_RANGE = 1.0;
+        const Y_RANGE = 1.0;
+
+        const vertices_data = [];
+
+        for(let x = 0; x <= X_RANGE; x += X_RESOLUTION) {
+          for(let y = 0; y <= Y_RANGE; y += Y_RESOLUTION) {
+            const distance = depthData.getDepthInMeters(x, y);
+
+            // We need to convert normalized view coordinates to normalized device coordinates,
+            // with the origin at the center of a cube with side length = 2 and Y growing upward.
+            const depth_coords_ndc = vec3.fromValues(x, y, 0.0);
+
+            // First, fix up the Y axis:
+            depth_coords_ndc[1] = 1 - depth_coords_ndc[1];
+
+            // Then, convert to range [-1, 1]:
+            depth_coords_ndc[0] = (2.0 * depth_coords_ndc[0]) - 1;
+            depth_coords_ndc[1] = (2.0 * depth_coords_ndc[1]) - 1;
+
+            if(depth_coords_ndc[0] > 1 || depth_coords_ndc[0] < -1 ||
+               depth_coords_ndc[1] > 1 || depth_coords_ndc[1] < -1) {
+              continue;
+            }
+
+            vertices_data.push(depth_coords_ndc[0], depth_coords_ndc[1], distance);
+          }
+        }
+
+        return vertices_data;
+      }
+
+      function renderDepthInformationCPU(depthData, view, viewport) {
+        const vertices_data = calculateVerticesFromViewCoordinates(depthData, viewport);
+
         gl.useProgram(programInfo.program);
 
         gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
diff --git a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
index 3e21fb1..b265757 100644
--- a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
+++ b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
@@ -29,10 +29,10 @@
 }
 
 void main(void) {
-  vec2 texCoord = (uUvTransform * vec4(vTexCoord.xy, 0, 1)).xy;
+  vec4 texCoord = uUvTransform * vec4(vTexCoord, 0, 1);
 
   highp float normalized_depth = clamp(
-    DepthGetMeters(uDepthTexture, texCoord) / kMaxDepthInMeters, 0.0, 1.0);
+    DepthGetMeters(uDepthTexture, texCoord.xy) / kMaxDepthInMeters, 0.0, 1.0);
   gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75);
 }
 
diff --git a/tools/clang/scripts/apply_edits.py b/tools/clang/scripts/apply_edits.py
index a49a739..abf95df 100755
--- a/tools/clang/scripts/apply_edits.py
+++ b/tools/clang/scripts/apply_edits.py
@@ -164,6 +164,59 @@
 '''
 
 
+_NEWLINE_CHARACTERS = [ord('\n'), ord('\r')]
+
+
+def _FindStartOfPreviousLine(contents, index):
+  """ Requires that `index` points to the start of a line.
+      Returns an index to the start of the previous line.
+  """
+  assert (index > 0)
+  assert (contents[index - 1] in _NEWLINE_CHARACTERS)
+
+  # Go back over the newline characters associated with the *single* end of a
+  # line just before `index`, despite of whether end of a line is designated by
+  # "\r", "\n" or "\r\n".  Examples:
+  # 1. "... \r\n <new index> \r\n <old index> ...
+  # 2. "... \n <new index> \n <old index> ...
+  index = index - 1
+  if index > 0 and contents[index - 1] in _NEWLINE_CHARACTERS and \
+      contents[index - 1] != contents[index]:
+    index = index - 1
+
+  # Go back until `index` points right after an end of a line (or at the
+  # beginning of the `contents`).
+  while index > 0 and contents[index - 1] not in _NEWLINE_CHARACTERS:
+    index = index - 1
+
+  return index
+
+
+def _SkipOverPreviousComment(contents, index):
+  """ Returns `index`, possibly moving it earlier so that it skips over comment
+      lines appearing in `contents` just before the old `index.
+
+      Example:
+          <returned `index` points here>// Comment
+                                        // Comment
+          <original `index` points here>bar
+  """
+  # If `index` points at the start of the file, or `index` doesn't point at the
+  # beginning of a line, then don't skip anything and just return `index`.
+  if index == 0 or contents[index - 1] not in _NEWLINE_CHARACTERS:
+    return index
+
+  # Is the previous line a non-comment?  If so, just return `index`.
+  new_index = _FindStartOfPreviousLine(contents, index)
+  prev_text = contents[new_index:index]
+  _COMMENT_START_REGEX = "^  \s*  (  //  |  \*  )"
+  if not re.search(_COMMENT_START_REGEX, prev_text, re.VERBOSE):
+    return index
+
+  # Otherwise skip over the previous line + continue skipping via recursion.
+  return _SkipOverPreviousComment(contents, new_index)
+
+
 def _InsertNonSystemIncludeHeader(filepath, header_line_to_add, contents):
   """ Mutates |contents| (contents of |filepath|) to #include
       the |header_to_add
@@ -185,7 +238,7 @@
   regex_text = _INCLUDE_INSERTION_POINT_REGEX_TEMPLATE % primary_header_basename
   match = re.search(regex_text, contents, re.MULTILINE | re.VERBOSE)
   assert (match is not None)
-  insertion_point = match.start()
+  insertion_point = _SkipOverPreviousComment(contents, match.start())
 
   # Extra empty line is required if the addition is not adjacent to other
   # includes.
diff --git a/tools/clang/scripts/apply_edits_test.py b/tools/clang/scripts/apply_edits_test.py
index d8edd45f..a2bc63b 100755
--- a/tools/clang/scripts/apply_edits_test.py
+++ b/tools/clang/scripts/apply_edits_test.py
@@ -56,6 +56,15 @@
 
 
 class InsertIncludeHeaderTest(unittest.TestCase):
+  def _assertEqualContents(self, expected, actual):
+    if expected != actual:
+      print("####################### EXPECTED:")
+      print(expected)
+      print("####################### ACTUAL:")
+      print(actual)
+      print("####################### END.")
+    self.assertEqual(expected, actual)
+
   def testSkippingCppComments(self):
     old_contents = '''
 // Copyright info here.
@@ -69,7 +78,144 @@
 #include "old/header.h"
     '''
     new_header_line = '#include "new/header.h'
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
+
+  def testSkippingCppComments_DocCommentForStruct(self):
+    """ This is a regression test for https://crbug.com/1175684 """
+    old_contents = '''
+// Copyright blah blah...
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
+
+#include <stdint.h>
+
+// Doc comment for a struct.
+// Multiline.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    expected_new_contents = '''
+// Copyright blah blah...
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
+
+#include <stdint.h>
+
+#include "new/header.h"
+
+// Doc comment for a struct.
+// Multiline.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    new_header_line = '#include "new/header.h'
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
+
+  def testSkippingCppComments_DocCommentForStruct2(self):
+    """ This is a regression test for https://crbug.com/1175684 """
+    old_contents = '''
+// Copyright blah blah...
+
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    expected_new_contents = '''
+// Copyright blah blah...
+
+#include "new/header.h"
+
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    new_header_line = '#include "new/header.h'
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
+
+  def testSkippingCppComments_DocCommentForStruct3(self):
+    """ This is a regression test for https://crbug.com/1175684 """
+    old_contents = '''
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    expected_new_contents = '''
+#include "new/header.h"
+
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    new_header_line = '#include "new/header.h'
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
+
+  def testSkippingCppComments_DocCommentForInclude(self):
+    """ This is a regression test for https://crbug.com/1175684 """
+    old_contents = '''
+// Copyright blah blah...
+
+// System includes.
+#include <stdint.h>
+
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    expected_new_contents = '''
+// Copyright blah blah...
+
+// System includes.
+#include <stdint.h>
+
+#include "new/header.h"
+
+// Doc comment for a struct.
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    new_header_line = '#include "new/header.h'
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
+
+  def testSkippingCppComments_DocCommentForWholeFile(self):
+    """ This is a regression test for https://crbug.com/1175684 """
+    old_contents = '''
+// Copyright blah blah...
+
+// Doc comment for the whole file.
+
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    expected_new_contents = '''
+// Copyright blah blah...
+
+// Doc comment for the whole file.
+
+#include "new/header.h"
+
+struct sock_filter {
+  uint16_t code;
+};
+    '''
+    new_header_line = '#include "new/header.h'
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingOldStyleComments(self):
     old_contents = '''
@@ -87,7 +233,8 @@
 #include "new/header.h"
 #include "old/header.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingOldStyleComments_NoWhitespaceAtLineStart(self):
     old_contents = '''
@@ -105,7 +252,8 @@
 #include "new/header.h"
 #include "old/header.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingSystemHeaders(self):
     old_contents = '''
@@ -121,7 +269,8 @@
 #include "new/header.h"
 #include "old/header.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingPrimaryHeader(self):
     old_contents = '''
@@ -139,7 +288,8 @@
 #include "new/header.h"
 #include "old/header.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSimilarNonPrimaryHeader_WithPrimaryHeader(self):
     old_contents = '''
@@ -159,7 +309,8 @@
 #include "new/header.h"
 #include "zzz/foo.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSimilarNonPrimaryHeader_NoPrimaryHeader(self):
     old_contents = '''
@@ -175,7 +326,8 @@
 #include "new/header.h"
 #include "zzz/foo.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingIncludeGuards(self):
     old_contents = '''
@@ -195,8 +347,9 @@
 
 #endif FOO_IMPL_H_
     '''
-    self.assertEqual(expected_new_contents,
-                     _InsertHeader(old_contents, 'foo/impl.h', 'new/header.h'))
+    self._assertEqualContents(
+        expected_new_contents,
+        _InsertHeader(old_contents, 'foo/impl.h', 'new/header.h'))
 
   def testSkippingIncludeGuards2(self):
     # This test is based on base/third_party/valgrind/memcheck.h
@@ -217,7 +370,8 @@
 
 #endif
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingIncludeGuards3(self):
     # This test is based on base/third_party/xdg_mime/xdgmime.h
@@ -256,7 +410,8 @@
 #endif /* __cplusplus */
 #endif /* __XDG_MIME_H__ */
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingIncludeGuards4(self):
     # This test is based on ash/first_run/desktop_cleaner.h and/or
@@ -285,7 +440,8 @@
 
 #endif  // ASH_FIRST_RUN_DESKTOP_CLEANER_
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingIncludeGuards5(self):
     # This test is based on third_party/weston/include/GLES2/gl2.h (the |extern
@@ -322,7 +478,8 @@
 
 #endif
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testSkippingIncludeGuards6(self):
     # This test is based on ios/third_party/blink/src/html_token.h
@@ -353,7 +510,8 @@
 
 #endif
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testNoOpIfAlreadyPresent(self):
     # This tests that the new header won't be inserted (and duplicated)
@@ -372,7 +530,8 @@
 #include "new/header.h"
 #include "new/header2.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testNoOpIfAlreadyPresent_WithTrailingComment(self):
     # This tests that the new header won't be inserted (and duplicated)
@@ -391,7 +550,8 @@
 #include "new/header.h" // blah
 #include "new/header2.h"
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testNoOldHeaders(self):
     # This tests that an extra new line is inserted after the new header
@@ -408,7 +568,8 @@
 
 struct S {};
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testPlatformIfDefs(self):
     # This test is based on
@@ -454,7 +615,8 @@
 
 namespace double_conversion {
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testNoOldIncludesAndIfDefs(self):
     # Artificial test: no old #includes + some #ifdefs.  The main focus of the
@@ -476,7 +638,8 @@
 
 void foo();
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testNoOldIncludesAndIfDefs2(self):
     # Artificial test: no old #includes + some #ifdefs.  The main focus of the
@@ -498,7 +661,8 @@
 
 void foo();
     '''
-    self.assertEqual(expected_new_contents, _InsertHeader(old_contents))
+    self._assertEqualContents(expected_new_contents,
+                              _InsertHeader(old_contents))
 
   def testUtf8BomMarker(self):
     # Test based on
@@ -523,12 +687,12 @@
     expected.extend(expected_new_contents.encode('utf-8'))
     # Test sanity check (i.e. not an assertion about code under test).
     utf8_bom = [0xef, 0xbb, 0xbf]
-    self.assertEqual(list(actual[0:3]), utf8_bom)
-    self.assertEqual(list(expected[0:3]), utf8_bom)
+    self._assertEqualContents(list(actual[0:3]), utf8_bom)
+    self._assertEqualContents(list(expected[0:3]), utf8_bom)
     # Actual test.
     edit = apply_edits.Edit('include-user-header', -1, -1, "new/header.h")
     apply_edits._ApplySingleEdit("foo/impl.cc", actual, edit, None)
-    self.assertEqual(expected, actual)
+    self._assertEqualContents(expected, actual)
 
 
 def _CreateReplacement(content_string, old_substring, new_substring):
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 5dafaa4..3b10d7a 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -395,8 +395,9 @@
     for d in ['asan-i386-Linux', 'asan-x86_64-Linux', 'lsan-i386-Linux',
               'lsan-x86_64-Linux', 'msan-x86_64-Linux', 'tsan-x86_64-Linux',
               'ubsan-i386-Linux', 'ubsan-x86_64-Linux']:
-      EnsureDirExists(os.path.join(sanitizer_common_tests, d))
-      CopyFile(libstdcpp, os.path.join(sanitizer_common_tests, d))
+      libpath = os.path.join(sanitizer_common_tests, d, 'Output', 'lib')
+      EnsureDirExists(libpath)
+      CopyFile(libstdcpp, libpath)
 
 
 def gn_arg(v):
@@ -681,11 +682,7 @@
     CopyLibstdcpp(args, LLVM_BOOTSTRAP_INSTALL_DIR)
     RunCommand(['ninja'], msvc_arch='x64')
     if args.run_tests:
-      test_targets = [ 'check-all' ]
-      if sys.platform == 'darwin':
-        # TODO(crbug.com/731375): Run check-all on Darwin too.
-        test_targets = [ 'check-llvm', 'check-clang', 'check-builtins' ]
-      RunCommand(['ninja'] + test_targets, msvc_arch='x64')
+      RunCommand(['ninja', 'check-all'], msvc_arch='x64')
     RunCommand(['ninja', 'install'], msvc_arch='x64')
 
     if sys.platform == 'win32':
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index abdc4e42..0a93132 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1632,10 +1632,21 @@
             '--logs-dir=${ISOLATED_OUTDIR}',
             '--',
         ]
-      cmdline += [
-          '../../testing/test_env.py',
-          '../../' + self.ToSrcRelPath(isolate_map[target]['script'])
-      ]
+      if is_android:
+        extra_files.append('../../build/android/test_wrapper/logdog_wrapper.py')
+        cmdline += [
+            '../../testing/test_env.py',
+            '../../build/android/test_wrapper/logdog_wrapper.py',
+            '--script',
+            '../../' + self.ToSrcRelPath(isolate_map[target]['script']),
+            '--logdog-bin-cmd',
+            '../../.task_template_packages/logdog_butler',
+        ]
+      else:
+        cmdline += [
+            '../../testing/test_env.py',
+            '../../' + self.ToSrcRelPath(isolate_map[target]['script'])
+        ]
     elif test_type == 'additional_compile_target':
       cmdline = [
           './' + str(target) + executable_suffix,
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index cadbdfe4..3b48caf3 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -14,10 +14,6 @@
   # this dict to look up which config to use for a given bot.
   'builder_groups': {
     'chrome': {
-      'chromeos-arm-generic-beta': 'official_chromeos_arm-generic',
-      'chromeos-arm-generic-ltc': 'official_chromeos_arm-generic',
-      'chromeos-arm-generic-lts': 'official_chromeos_arm-generic',
-      'chromeos-arm-generic-stable': 'official_chromeos_arm-generic',
       'chromeos-arm-generic-cfi-thin-lto-chrome': 'chromeos_arm-generic_cfi_thin_lto_official',
       'chromeos-betty-pi-arc-cfi-thin-lto-chrome': 'chromeos_betty-pi-arc_cfi_thin_lto_official',
       'chromeos-betty-pi-arc-chrome': 'chromeos_betty-pi-arc_include_unwind_tables_official_use_fake_dbus_clients',
@@ -1976,27 +1972,27 @@
     ],
 
     'gpu_fyi_tests_debug_trybot': [
-      'gpu_fyi_tests', 'debug_bot',
+      'gpu_fyi_tests', 'debug_bot', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_debug_trybot_x86': [
-      'gpu_fyi_tests', 'debug_bot', 'x86',
+      'gpu_fyi_tests', 'debug_bot', 'x86', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_dx12vk_debug_trybot': [
-      'gpu_fyi_tests', 'dx12vk', 'debug_bot',
+      'gpu_fyi_tests', 'dx12vk', 'debug_bot', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_dx12vk_release_trybot': [
-      'gpu_fyi_tests', 'dx12vk', 'release_trybot',
+      'gpu_fyi_tests', 'dx12vk', 'release_trybot', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot': [
-      'gpu_fyi_tests', 'release_trybot',
+      'gpu_fyi_tests', 'release_trybot', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot_arm64': [
-      'gpu_fyi_tests', 'release_trybot', 'arm64',
+      'gpu_fyi_tests', 'release_trybot', 'arm64', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot_asan': [
@@ -2004,7 +2000,7 @@
     ],
 
     'gpu_fyi_tests_release_trybot_fuchsia': [
-      'gpu_fyi_tests', 'release_trybot', 'fuchsia',
+      'gpu_fyi_tests', 'release_trybot', 'fuchsia', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot_tsan': [
@@ -2012,7 +2008,7 @@
     ],
 
     'gpu_fyi_tests_release_trybot_x86': [
-      'gpu_fyi_tests', 'release_trybot', 'x86',
+      'gpu_fyi_tests', 'release_trybot', 'x86', 'disable_nacl',
     ],
 
     'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild': [
@@ -2337,10 +2333,6 @@
       'msan', 'release_bot_blink',
     ],
 
-    'official_chromeos_arm-generic': [
-      'chromeos_device', 'arm-generic', 'goma', 'official_optimize',
-    ],
-
     'official_celab_release_bot': [
       'official', 'release_bot', 'minimal_symbols',
     ],
@@ -2738,7 +2730,7 @@
 
     'angle_specific_tests': {
       'mixins': ['angle_gles1_conform_tests', 'angle_deqp_tests',
-                 'angle_trace_perf_tests'],
+                 'angle_trace_perf_tests', 'disable_nacl'],
     },
 
     'angle_trace_perf_tests': {
diff --git a/tools/mb/mb_config_expectations/chrome.json b/tools/mb/mb_config_expectations/chrome.json
index e0deafa5..fc45870 100644
--- a/tools/mb/mb_config_expectations/chrome.json
+++ b/tools/mb/mb_config_expectations/chrome.json
@@ -1,13 +1,4 @@
 {
-  "chromeos-arm-generic-beta": {
-    "args_file": "//build/args/chromeos/arm-generic.gni",
-    "gn_args": {
-      "is_chromeos_device": true,
-      "is_official_build": true,
-      "ozone_platform_headless": true,
-      "use_goma": true
-    }
-  },
   "chromeos-arm-generic-cfi-thin-lto-chrome": {
     "args_file": "//build/args/chromeos/arm-generic.gni",
     "gn_args": {
@@ -22,33 +13,6 @@
       "use_thin_lto": true
     }
   },
-  "chromeos-arm-generic-ltc": {
-    "args_file": "//build/args/chromeos/arm-generic.gni",
-    "gn_args": {
-      "is_chromeos_device": true,
-      "is_official_build": true,
-      "ozone_platform_headless": true,
-      "use_goma": true
-    }
-  },
-  "chromeos-arm-generic-lts": {
-    "args_file": "//build/args/chromeos/arm-generic.gni",
-    "gn_args": {
-      "is_chromeos_device": true,
-      "is_official_build": true,
-      "ozone_platform_headless": true,
-      "use_goma": true
-    }
-  },
-  "chromeos-arm-generic-stable": {
-    "args_file": "//build/args/chromeos/arm-generic.gni",
-    "gn_args": {
-      "is_chromeos_device": true,
-      "is_official_build": true,
-      "ozone_platform_headless": true,
-      "use_goma": true
-    }
-  },
   "chromeos-betty-pi-arc-cfi-thin-lto-chrome": {
     "args_file": "//build/args/chromeos/betty-pi-arc.gni",
     "gn_args": {
diff --git a/tools/mb/mb_config_expectations/chromium.angle.json b/tools/mb/mb_config_expectations/chromium.angle.json
index 402c2e3..e72935f 100644
--- a/tools/mb/mb_config_expectations/chromium.angle.json
+++ b/tools/mb/mb_config_expectations/chromium.angle.json
@@ -5,6 +5,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -37,6 +38,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": false,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -53,6 +55,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -68,6 +71,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -82,6 +86,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -95,6 +100,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "enable_run_ios_unittests_with_xctest": true,
       "ios_set_attributes_for_xcode_project_generation": false,
       "is_component_build": false,
@@ -112,6 +118,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -135,6 +142,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "ozone_platform": "headless",
@@ -153,6 +161,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -200,6 +209,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
@@ -212,6 +222,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "is_component_build": true,
       "is_debug": false,
       "symbol_level": 1,
diff --git a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
index 0c31de3..f5b59fe 100644
--- a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
@@ -260,6 +260,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -273,6 +274,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -317,6 +319,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -330,6 +333,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -371,6 +375,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -385,6 +390,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -411,6 +417,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -424,6 +431,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -438,6 +446,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "enable_vulkan": true,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
@@ -452,6 +461,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "enable_vulkan": true,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
@@ -477,6 +487,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -536,6 +547,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
index 814acf89..e36c6333 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
@@ -96,6 +96,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -112,6 +113,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "enable_run_ios_unittests_with_xctest": true,
       "ios_set_attributes_for_xcode_project_generation": false,
       "is_component_build": false,
@@ -128,6 +130,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -172,6 +175,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -207,6 +211,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -222,6 +227,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index b9c5597..5060a6e4 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -157,6 +157,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -181,6 +182,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -195,6 +197,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -222,6 +225,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -235,6 +239,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -259,6 +264,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -273,6 +279,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -287,6 +294,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -822,6 +830,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
index bfef7290..0dfe375d 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
@@ -14,6 +14,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -27,6 +28,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -41,6 +43,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -55,6 +58,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -69,6 +73,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -99,6 +104,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -123,6 +129,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -137,6 +144,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -151,6 +159,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -164,6 +173,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -178,6 +188,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -192,6 +203,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -564,6 +576,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.win.json b/tools/mb/mb_config_expectations/tryserver.chromium.win.json
index b0579d6..86512e7 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.win.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.win.json
@@ -4,6 +4,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -28,6 +29,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -42,6 +44,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -55,6 +58,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -78,6 +82,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "enable_vulkan": true,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
@@ -93,6 +98,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "enable_vulkan": true,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
@@ -108,6 +114,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -122,6 +129,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -137,6 +145,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -163,6 +172,7 @@
     "gn_args": {
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": true,
@@ -189,6 +199,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -214,6 +225,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -229,6 +241,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
@@ -412,6 +425,7 @@
       "build_angle_gles1_conform_tests": true,
       "build_angle_trace_perf_tests": true,
       "dcheck_always_on": true,
+      "enable_nacl": false,
       "ffmpeg_branding": "Chrome",
       "internal_gles2_conform_tests": true,
       "is_component_build": false,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index b5e65f3..2a22107 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -27206,6 +27206,29 @@
   </description>
 </action>
 
+<action name="WindowNaming_Cleared">
+  <owner>ellyjones@chromium.org</owner>
+  <description>
+    Logged when the user accepts the &quot;Name Window...&quot; prompt to clear
+    a custom name for a browser window.
+  </description>
+</action>
+
+<action name="WindowNaming_DialogShown">
+  <owner>ellyjones@chromium.org</owner>
+  <description>
+    Logged when the user opens the &quot;Name Window...&quot; prompt.
+  </description>
+</action>
+
+<action name="WindowNaming_Set">
+  <owner>ellyjones@chromium.org</owner>
+  <description>
+    Logged when the user accepts the &quot;Name Window...&quot; prompt to set a
+    custom name for a browser window.
+  </description>
+</action>
+
 <action name="WindowSelector_ActiveWindowChanged">
   <owner>tbuckley@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ae3daf6..4bae998 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12784,6 +12784,7 @@
   <int value="14" label="CONTEXT_LOST_SET_DRAW_RECTANGLE_FAILED"/>
   <int value="15" label="CONTEXT_LOST_DIRECT_COMPOSITION_OVERLAY_FAILED"/>
   <int value="16" label="CONTEXT_LOST_SWAP_FAILED"/>
+  <int value="17" label="CONTEXT_LOST_BEGIN_PAINT_FAILED"/>
 </enum>
 
 <enum name="ContextMenuDelayedElementDetails">
@@ -26331,6 +26332,7 @@
   <int value="1539" label="FILEMANAGERPRIVATE_ISTABLETMODEENABLED"/>
   <int value="1540" label="FILEMANAGERPRIVATE_NOTIFYDRIVEDIALOGRESULT"/>
   <int value="1541" label="ENTERPRISEREPORTINGPRIVATE_GETCONTEXTINFO"/>
+  <int value="1542" label="SCRIPTING_REMOVECSS"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -42825,6 +42827,7 @@
   <int value="-2104654357" label="GamesHub:enabled"/>
   <int value="-2102286055" label="WebViewVulkanIntermediateBuffer:disabled"/>
   <int value="-2101682955" label="EnableNotificationIndicator:enabled"/>
+  <int value="-2101338272" label="EnablePciguardUi:disabled"/>
   <int value="-2101337189" label="AutofillOffNoServerData:disabled"/>
   <int value="-2099486626" label="DownloadLater:enabled"/>
   <int value="-2099457894" label="Mash:enabled"/>
@@ -43090,6 +43093,7 @@
   <int value="-1880355454" label="disable-topchrome-md"/>
   <int value="-1879877238" label="disable-cancel-all-touches"/>
   <int value="-1878958319" label="ScrollableTabStripButtons:disabled"/>
+  <int value="-1876955850" label="EnablePciguardUi:enabled"/>
   <int value="-1876881908"
       label="disable-infobar-for-protected-media-identifier"/>
   <int value="-1875383510" label="UseGoogleLocalNtp:disabled"/>
@@ -44558,6 +44562,7 @@
   <int value="-561194974" label="AutofillExpandedPopupViews:enabled"/>
   <int value="-560551550" label="use-memory-pressure-chromeos"/>
   <int value="-560114351" label="OfflinePagesRenovations:disabled"/>
+  <int value="-558488712" label="PreemtiveLinkToTextGeneration:disabled"/>
   <int value="-558471324" label="PreviewsCoinFlipHoldback_UKMOnly:disabled"/>
   <int value="-557742250" label="ContentSuggestionsCategories:disabled"/>
   <int value="-556218705" label="SlowDCTimerInterruptsWin:enabled"/>
@@ -46693,6 +46698,7 @@
       label="OmniboxRemoveSuggestionsFromClipboard:disabled"/>
   <int value="1442798825" label="enable-quic"/>
   <int value="1442830837" label="MemoryAblation:disabled"/>
+  <int value="1446349255" label="ArcEnableUsap:disabled"/>
   <int value="1446946673" label="DesktopRestructuredLanguageSettings:disabled"/>
   <int value="1447295459" label="SyncPseudoUSSApps:enabled"/>
   <int value="1448684258" label="TabHoverCardImages:enabled"/>
@@ -46715,6 +46721,7 @@
   <int value="1460747747" label="GdiTextPrinting:enabled"/>
   <int value="1460958818" label="NTPForeignSessionsSuggestions:enabled"/>
   <int value="1461581256" label="MovablePartialScreenshot:enabled"/>
+  <int value="1463230871" label="ArcEnableUsap:enabled"/>
   <int value="1464028544" label="WinUseHybridSpellChecker:disabled"/>
   <int value="1464610065" label="TabbedAppOverflowMenuRegroup:disabled"/>
   <int value="1465624446" label="disable-zero-copy"/>
@@ -46853,6 +46860,7 @@
   <int value="1600926040" label="TranslateCompactUI:enabled"/>
   <int value="1601582484" label="enable-crash-reporter-for-testing"/>
   <int value="1602627012" label="OverrideSitePrefsForHrefTranslate:enabled"/>
+  <int value="1602791752" label="PreemtiveLinkToTextGeneration:enabled"/>
   <int value="1602869271" label="ChromeShareScreenshot:disabled"/>
   <int value="1603578716" label="CaptionSettings:disabled"/>
   <int value="1604893983" label="VizForWebView:disabled"/>
@@ -72845,6 +72853,12 @@
   <int value="2" label="Parse error"/>
 </enum>
 
+<enum name="SubresourceWebBundleLoadResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Metadata parse error"/>
+  <int value="2" label="Memory quota exceeded"/>
+</enum>
+
 <enum name="SuccessTimeoutStarted">
   <int value="0" label="Success"/>
   <int value="1" label="Timeout"/>
diff --git a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
index c8b73a6..0bfc33c 100644
--- a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
@@ -265,7 +265,7 @@
 </histogram>
 
 <histogram name="Accessibility.CrosHighContrast" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
@@ -508,7 +508,7 @@
 </histogram>
 
 <histogram name="Accessibility.ImageLabels.ModalDialogAccepted"
-    enum="BooleanAccepted" expires_after="2021-06-06">
+    enum="BooleanAccepted" expires_after="2021-08-09">
   <owner>katie@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
   <summary>
@@ -531,7 +531,7 @@
 </histogram>
 
 <histogram name="Accessibility.ImageLabels.RequestLanguage" enum="LanguageCode"
-    expires_after="2021-03-28">
+    expires_after="2021-08-09">
   <owner>katie@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
   <owner>chrome-a11y-core@google.com</owner>
@@ -543,7 +543,7 @@
 </histogram>
 
 <histogram name="Accessibility.iOS.NewLargerTextCategory" enum="BooleanHit"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>gambard@chromium.org</owner>
   <owner>rkgibson@google.com</owner>
   <summary>
@@ -558,7 +558,7 @@
 </histogram>
 
 <histogram name="Accessibility.LanguageDetection.CountDetectionAttempted"
-    units="count" expires_after="2021-05-23">
+    units="count" expires_after="2021-08-09">
   <owner>chrishall@chromium.org</owner>
   <owner>aboxhall@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
@@ -568,7 +568,7 @@
 </histogram>
 
 <histogram name="Accessibility.LanguageDetection.CountLabelled" units="count"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>chrishall@chromium.org</owner>
   <owner>aboxhall@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
@@ -579,7 +579,7 @@
 </histogram>
 
 <histogram name="Accessibility.LanguageDetection.LangsPerPage" units="count"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>chrishall@chromium.org</owner>
   <owner>aboxhall@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
@@ -591,7 +591,7 @@
 </histogram>
 
 <histogram name="Accessibility.LanguageDetection.PercentageLabelledWithTop"
-    units="%" expires_after="2021-05-23">
+    units="%" expires_after="2021-08-09">
   <owner>chrishall@chromium.org</owner>
   <owner>aboxhall@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
@@ -614,7 +614,7 @@
 </histogram>
 
 <histogram name="Accessibility.LanguageDetection.PercentageOverridden"
-    units="%" expires_after="2021-05-23">
+    units="%" expires_after="2021-08-09">
   <owner>chrishall@chromium.org</owner>
   <owner>aboxhall@chromium.org</owner>
   <owner>dmazzoni@chromium.org</owner>
@@ -895,7 +895,7 @@
 </histogram>
 
 <histogram name="Accessibility.Shortcuts.CrosDockedMagnifier"
-    enum="BooleanEnabled" expires_after="2021-05-23">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>amraboelkher@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
   <summary>
@@ -905,7 +905,7 @@
 </histogram>
 
 <histogram name="Accessibility.Shortcuts.CrosHighContrast"
-    enum="BooleanEnabled" expires_after="2021-05-23">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>amraboelkher@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
   <summary>
@@ -915,7 +915,7 @@
 </histogram>
 
 <histogram name="Accessibility.Shortcuts.CrosScreenMagnifier"
-    enum="BooleanEnabled" expires_after="2021-05-23">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>amraboelkher@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
   <summary>
@@ -925,7 +925,7 @@
 </histogram>
 
 <histogram name="Accessibility.Shortcuts.CrosSpokenFeedback"
-    enum="BooleanEnabled" expires_after="2021-05-23">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>amraboelkher@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
   <summary>
@@ -1180,7 +1180,7 @@
 </histogram>
 
 <histogram name="DomDistiller.ReaderMode.EntryPoint"
-    enum="ReaderModeEntryPoint" expires_after="2021-06-06">
+    enum="ReaderModeEntryPoint" expires_after="2021-08-09">
   <owner>katie@chromium.org</owner>
   <owner>gilmanmh@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
@@ -1188,7 +1188,7 @@
 </histogram>
 
 <histogram name="DomDistiller.ReaderMode.ExitPoint" enum="ReaderModeEntryPoint"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>katie@chromium.org</owner>
   <owner>gilmanmh@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index 9a140cc..edae4bf 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -620,7 +620,7 @@
 </histogram>
 
 <histogram name="Android.DeviceSize.LargestDisplaySize" units="dp"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
@@ -632,7 +632,7 @@
 </histogram>
 
 <histogram name="Android.DeviceSize.SmallestDisplaySize" units="dp"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
@@ -683,7 +683,7 @@
 </histogram>
 
 <histogram name="Android.Download.Rename.Dialog.Action"
-    enum="Android.Download.Rename.Dialog.Action" expires_after="2021-05-30">
+    enum="Android.Download.Rename.Dialog.Action" expires_after="2021-08-09">
   <owner>hesen@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -693,7 +693,7 @@
 </histogram>
 
 <histogram name="Android.Download.Rename.Result"
-    enum="Android.Download.Rename.Result" expires_after="2021-05-30">
+    enum="Android.Download.Rename.Result" expires_after="2021-08-09">
   <owner>hesen@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -808,7 +808,7 @@
 </histogram>
 
 <histogram name="Android.DownloadManager.OpenSource.Audio"
-    enum="AndroidDownloadOpenSource" expires_after="2021-06-06">
+    enum="AndroidDownloadOpenSource" expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>Records how users open audio download files on Android.</summary>
@@ -1519,7 +1519,7 @@
 </histogram>
 
 <histogram name="Android.NTP.Impression" enum="NTPImpressionType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>finkm@chromium.org</owner>
   <summary>
     Counts impressions of the NTP on Android. It also counts potential
@@ -1830,7 +1830,7 @@
 </histogram>
 
 <histogram name="Android.PhotoPicker.EnumerationTime" units="ms"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>finnur@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -2079,7 +2079,7 @@
 </histogram>
 
 <histogram name="Android.ShouldDestroyIncognitoProfileOnStartup"
-    units="Boolean" expires_after="M92">
+    units="Boolean" expires_after="2021-08-09">
   <owner>rhalavati@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/apps/histograms.xml b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
index b3acb55..979bbe5 100644
--- a/tools/metrics/histograms/histograms_xml/apps/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
@@ -411,7 +411,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.SearchQueryLength.Apps" units="characters"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>thanhdng@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <summary>
@@ -740,7 +740,7 @@
 </histogram>
 
 <histogram name="Apps.AppListAppMovingType" enum="AppListAppMovingType"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>mmourgos@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -871,7 +871,7 @@
 </histogram>
 
 <histogram name="Apps.AppListPageOpened" enum="AppListPage"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>calamity@chromium.org</owner>
   <owner>src/ash/app_list/OWNERS</owner>
   <summary>
@@ -1056,7 +1056,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchQueryLength" units="characters"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>calamity@chromium.org</owner>
@@ -1138,7 +1138,7 @@
 </histogram>
 
 <histogram name="Apps.AppListStateTransitionSource"
-    enum="AppListStateTransitionSource" expires_after="2021-05-02">
+    enum="AppListStateTransitionSource" expires_after="2021-08-09">
   <owner>newcomer@chromium.org</owner>
   <owner>mmourgos@chromium.org</owner>
   <summary>
@@ -1185,7 +1185,7 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.AppsInFolders" units="Apps" expires_after="2021-04-04">
+<histogram name="Apps.AppsInFolders" units="Apps" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes
      name="AppListFolderExperiment" -->
 
@@ -1223,7 +1223,7 @@
 </histogram>
 
 <histogram base="true" name="Apps.ContextMenuShowSource" enum="MenuSourceType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes
      name="AppUIComponent" -->
 
@@ -1278,7 +1278,7 @@
 </histogram>
 
 <histogram base="true" name="Apps.DefaultAppLaunch" enum="DefaultAppName"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="DefaultAppLaunchSource" -->
 
   <owner>dominickn@chromium.org</owner>
@@ -1645,7 +1645,7 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.NumberOfPages" units="page(s)" expires_after="M92">
+<histogram name="Apps.NumberOfPages" units="page(s)" expires_after="2021-08-09">
   <owner>mmourgos@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -1654,7 +1654,8 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.NumberOfPagesNotFull" units="page(s)" expires_after="M92">
+<histogram name="Apps.NumberOfPagesNotFull" units="page(s)"
+    expires_after="2021-08-09">
   <owner>mmourgos@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -1664,7 +1665,7 @@
 </histogram>
 
 <histogram name="Apps.PaginationTransition.AnimationSmoothness" units="%"
-    expires_after="M92">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>newcomer@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/arc/histograms.xml b/tools/metrics/histograms/histograms_xml/arc/histograms.xml
index 5a9f340..8362e83d 100644
--- a/tools/metrics/histograms/histograms_xml/arc/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/arc/histograms.xml
@@ -138,7 +138,7 @@
 </histogram>
 
 <histogram name="Arc.AppListRecommendedImp.AllImpression" units="count"
-    expires_after="2021-05-30">
+    expires_after="2021-08-09">
   <owner>robsc@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
@@ -157,7 +157,8 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.AppShortcuts.BuildMenuTime" units="ms" expires_after="M92">
+<histogram name="Arc.AppShortcuts.BuildMenuTime" units="ms"
+    expires_after="2021-08-09">
   <owner>mmourgos@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -445,7 +446,7 @@
 </histogram>
 
 <histogram name="Arc.EngagementTime.Background" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>maajid@google.com</owner>
   <owner>shaochuan@google.com</owner>
   <owner>shihuis@google.com</owner>
@@ -680,7 +681,7 @@
 </histogram>
 
 <histogram name="Arc.OptInCancel" enum="ArcOptInCancel"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>elijahtaylor@google.com</owner>
   <owner>shihuis@google.com</owner>
   <summary>Arc OptIn cancelation reason.</summary>
@@ -711,7 +712,7 @@
 </histogram>
 
 <histogram name="Arc.OptInSilentAuthCode.SecondaryAccount"
-    enum="ArcOptInSilentAuthCode" expires_after="2021-04-04">
+    enum="ArcOptInSilentAuthCode" expires_after="2021-08-09">
   <owner>khmel@google.com</owner>
   <summary>
     Arc Silent Auth Code status. This status is set during the minting of an
@@ -780,7 +781,7 @@
 </histogram>
 
 <histogram name="Arc.PlayStoreSearch.ReturnedAppsTotal" units="apps"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>hejq@chromium.org</owner>
   <summary>
     The total number of returned apps of a Play Store app discovery query.
@@ -959,7 +960,7 @@
 </histogram>
 
 <histogram name="Arc.Runtime.Performance.CommitDeviation" units="microseconds"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" -->
 
   <owner>khmel@google.com</owner>
@@ -1017,7 +1018,7 @@
 </histogram>
 
 <histogram name="Arc.SdkVersionUpgradeType" enum="ArcSdkVersionUpgradeType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>niwa@google.com</owner>
   <owner>yusukes@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 16dfa409..0fe28529 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -634,7 +634,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.MoveWindowFromActiveDesk"
-    enum="DesksMoveWindowFromActiveDeskSource" expires_after="2021-04-25">
+    enum="DesksMoveWindowFromActiveDeskSource" expires_after="2021-08-09">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -981,7 +981,7 @@
 </histogram>
 
 <histogram name="Ash.HotseatGesture" enum="HotseatInAppGesture"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>andrewxu@chromium.org</owner>
   <owner>tbarzic@chromium.org</owner>
   <summary>Gestures supported by the in-app hotseat.</summary>
@@ -1112,7 +1112,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.NbPasswordAttempts.UntilFailure"
-    units="attempts" expires_after="2021-05-27">
+    units="attempts" expires_after="2021-08-09">
   <owner>tellier@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -1196,7 +1196,7 @@
 </histogram>
 
 <histogram name="Ash.Login.OOBE.UserClicks" enum="OobeUserClickTarget"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>raleksandrov@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>What shelf buttons or trays are clicked in the OOBE.</summary>
@@ -1359,7 +1359,7 @@
 </histogram>
 
 <histogram name="Ash.NumberOfVisibleWindowsInPrimaryDisplay" units="Windows"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jamescook@chromium.org</owner>
   <summary>
     An upper bound on the number of windows visible to the user on the primary
@@ -1988,7 +1988,8 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.SplitView.TimeInSplitView" units="ms" expires_after="M92">
+<histogram name="Ash.SplitView.TimeInSplitView" units="ms"
+    expires_after="2021-08-09">
   <owner>xdai@chromium.org</owner>
   <summary>
     The amount of time that the user spent in split view mode. The time is
@@ -2073,7 +2074,7 @@
 </histogram>
 
 <histogram name="Ash.SwipeHomeToOverviewGesture"
-    enum="SwipeHomeToOverviewResult" expires_after="2021-04-04">
+    enum="SwipeHomeToOverviewResult" expires_after="2021-08-09">
   <owner>andrewxu@chromium.org</owner>
   <owner>tbarzic@chromium.org</owner>
   <summary>
@@ -2185,13 +2186,13 @@
 </histogram>
 
 <histogram name="Ash.TouchView.TouchViewActivePercentage" units="%"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>girard@chromium.org</owner>
   <summary>The proportion of time spent in TouchView during a session.</summary>
 </histogram>
 
 <histogram name="Ash.TouchView.TouchViewActiveTotal" units="minutes"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>girard@chromium.org</owner>
   <summary>The total time that TouchView is active during a session.</summary>
 </histogram>
@@ -2247,7 +2248,7 @@
 </histogram>
 
 <histogram name="Ash.Wallpaper.CustomLayout" enum="WallpaperLayout"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>xdai@chromium.org</owner>
   <summary>
     The custom wallpaper layout type. Recorded when the user sets a new custom
@@ -2255,7 +2256,8 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Wallpaper.Source" enum="WallpaperType" expires_after="M92">
+<histogram name="Ash.Wallpaper.Source" enum="WallpaperType"
+    expires_after="2021-08-09">
   <owner>xdai@chromium.org</owner>
   <summary>
     Recorded when a new wallpaper is set, either by the built-in Wallpaper
@@ -2264,7 +2266,8 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Wallpaper.Type" enum="WallpaperType" expires_after="M92">
+<histogram name="Ash.Wallpaper.Type" enum="WallpaperType"
+    expires_after="2021-08-09">
   <owner>kuscher@google.com</owner>
   <summary>The wallpaper type. Recorded at user login.</summary>
 </histogram>
@@ -2359,7 +2362,7 @@
 </histogram>
 
 <histogram name="Ash.WindowCycleController.TimeBetweenTaskSwitches"
-    units="seconds" expires_after="2021-05-16">
+    units="seconds" expires_after="2021-08-09">
   <owner>sammiequon@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
@@ -2382,7 +2385,7 @@
 </histogram>
 
 <histogram name="Ash.WindowCycleView.AnimationSmoothness.Show" units="%"
-    expires_after="2021-05-31">
+    expires_after="2021-08-09">
   <owner>yjliu@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
   <summary>
@@ -2394,7 +2397,7 @@
 </histogram>
 
 <histogram name="Ash.WindowDragFromShelfResult" enum="ShelfWindowDragResult"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>andrewxu@chromium.org</owner>
   <owner>tbarzic@chromium.org</owner>
   <summary>
@@ -2555,7 +2558,7 @@
 
 <histogram
     name="Ash.WorkspaceWindowResizer.TabDragging.PresentationTime.MaxLatency.ClamshellMode"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>yichenz@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/assistant/histograms.xml b/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
index 359c5c4..621e584 100644
--- a/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
@@ -147,14 +147,14 @@
 </histogram>
 
 <histogram name="Assistant.QueryResponseType" enum="AssistantQueryResponseType"
-    expires_after="2021-04-11">
+    expires_after="2021-08-09">
   <owner>xiaohuic@chromium.org</owner>
   <owner>meilinw@chromium.org</owner>
   <summary>The Assistant query response type.</summary>
 </histogram>
 
 <histogram name="Assistant.QuerySource" enum="AssistantQuerySource"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>xiaohuic@chromium.org</owner>
   <owner>meilinw@chromium.org</owner>
   <summary>
@@ -192,7 +192,7 @@
 </histogram>
 
 <histogram name="Assistant.SetDspHotwordLocale" enum="BooleanSuccess"
-    expires_after="2021-05-14">
+    expires_after="2021-08-09">
   <owner>meilinw@chromium.org</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -272,7 +272,7 @@
 </histogram>
 
 <histogram base="true" name="QuickAnswers.Consent.Duration" units="ms"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/autofill/histograms.xml b/tools/metrics/histograms/histograms_xml/autofill/histograms.xml
index a690d614..6f90860 100644
--- a/tools/metrics/histograms/histograms_xml/autofill/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/autofill/histograms.xml
@@ -22,7 +22,7 @@
 <histograms>
 
 <histogram name="Autofill.Address.IsEnabled.PageLoad" enum="BooleanEnabled"
-    expires_after="2021-04-01">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>chrome-autofill@google.com</owner>
   <summary>
@@ -32,7 +32,7 @@
 </histogram>
 
 <histogram name="Autofill.Address.IsEnabled.Startup" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>chrome-autofill@google.com</owner>
   <summary>
@@ -324,7 +324,7 @@
 
 <histogram
     name="Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn.Duration"
-    units="ms" expires_after="2021-05-31">
+    units="ms" expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>manasverma@google.com</owner>
   <owner>autofill-auth-team@google.com</owner>
@@ -392,7 +392,7 @@
 </histogram>
 
 <histogram name="Autofill.CardUnmask.CvcLength" units="digits"
-    expires_after="2021-03-31">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -417,7 +417,7 @@
 </histogram>
 
 <histogram name="Autofill.CardUploadEnabled" enum="AutofillCardUploadEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>aneeshali@google.com</owner>
   <owner>jsaul@google.com</owner>
   <summary>
@@ -428,7 +428,7 @@
 </histogram>
 
 <histogram name="Autofill.CreditCard.IsEnabled.PageLoad" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>chrome-autofill@google.com</owner>
   <summary>
@@ -438,7 +438,7 @@
 </histogram>
 
 <histogram name="Autofill.CreditCard.IsEnabled.Startup" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>chrome-autofill@google.com</owner>
   <summary>
@@ -592,7 +592,7 @@
 </histogram>
 
 <histogram name="Autofill.ExpirationDateFixFlowPromptShown" enum="Boolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>siashah@google.com</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -972,7 +972,7 @@
 </histogram>
 
 <histogram name="Autofill.IsEnabled.Startup" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>battre@chromium.org</owner>
   <owner>chrome-autofill@google.com</owner>
@@ -1159,7 +1159,7 @@
 </histogram>
 
 <histogram name="Autofill.ManageCardsPrompt" enum="AutofillManageCardsPrompt"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>manasverma@google.com</owner>
   <owner>jsaul@google.com</owner>
   <summary>
@@ -1497,7 +1497,7 @@
 </histogram>
 
 <histogram name="Autofill.ServerCardLinkClicked" enum="AutofillSyncState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>battre@chromium.org</owner>
   <owner>chrome-autofill@google.com</owner>
@@ -1775,7 +1775,7 @@
 </histogram>
 
 <histogram name="Autofill.UnmaskPrompt.Duration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <summary>
@@ -1786,7 +1786,7 @@
 </histogram>
 
 <histogram name="Autofill.UnmaskPrompt.Events" enum="AutofillUnmaskPromptEvent"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <summary>
@@ -1796,7 +1796,7 @@
 </histogram>
 
 <histogram name="Autofill.UnmaskPrompt.GetRealPanDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <summary>
@@ -1807,7 +1807,7 @@
 </histogram>
 
 <histogram name="Autofill.UnmaskPrompt.GetRealPanResult"
-    enum="AutofillGetRealPanResult" expires_after="2021-06-06">
+    enum="AutofillGetRealPanResult" expires_after="2021-08-09">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/background/histograms.xml b/tools/metrics/histograms/histograms_xml/background/histograms.xml
index 307d0ca..64e4ed20 100644
--- a/tools/metrics/histograms/histograms_xml/background/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/background/histograms.xml
@@ -340,7 +340,7 @@
 </histogram>
 
 <histogram name="BackgroundSync.Registration.OneShot"
-    enum="BackgroundSyncStatus" expires_after="2021-06-06">
+    enum="BackgroundSyncStatus" expires_after="2021-08-09">
   <owner>nator@chromium.org</owner>
   <owner>rayankans@chromium.org</owner>
   <summary>
@@ -349,7 +349,7 @@
 </histogram>
 
 <histogram name="BackgroundSync.Registration.OneShot.CouldFire"
-    enum="BooleanCouldFireImmediately" expires_after="2021-04-25">
+    enum="BooleanCouldFireImmediately" expires_after="2021-08-09">
   <owner>nator@chromium.org</owner>
   <owner>rayankans@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index ef7309a5..412904c6 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -74,7 +74,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.ContentChangeMode"
-    enum="BooleanContentChangeMode" expires_after="2021-05-09">
+    enum="BooleanContentChangeMode" expires_after="2021-08-09">
   <owner>yiyix@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -86,7 +86,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.ContextType" enum="CanvasContextType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>fserb@chromium.org</owner>
   <owner>aaronhk@chromium.org</owner>
   <summary>
@@ -104,7 +104,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.DrawImage.Duration"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>fserb@chromium.org</owner>
   <owner>aaronhk@chromium.org</owner>
   <summary>
@@ -117,7 +117,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.DrawImage.SqrtNumberOfPixels"
-    units="sqrt(pixels)" expires_after="2021-06-06">
+    units="sqrt(pixels)" expires_after="2021-08-09">
   <owner>fserb@chromium.org</owner>
   <owner>aaronhk@chromium.org</owner>
   <summary>
@@ -186,7 +186,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.MaximumInflightResources"
-    units="canvas resources" expires_after="2021-04-04">
+    units="canvas resources" expires_after="2021-08-09">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -197,7 +197,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.NumCanvasesPerPage" units="canvases"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -694,7 +694,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.DisplayLockIntersectionObserver.UpdateTime"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -855,6 +855,16 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Fonts.FontMatchAttempts.System" units="attempts"
+    expires_after="2021-12-20">
+  <owner>caraitto@chromium.org</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    Number of system font match attempts (success + failure) made via @font-face
+    src:local(), from page load to page unload. Recorded on page unload.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Fonts.ShapeCache" units="units"
     expires_after="2021-10-15">
   <owner>drott@chromium.org</owner>
@@ -1263,7 +1273,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.JavascriptIntersectionObserver.UpdateTime"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1376,7 +1386,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.LazyLoadIntersectionObserver.UpdateTime"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1673,7 +1683,7 @@
 </histogram>
 
 <histogram base="true" name="Blink.MediaIntersectionObserver.UpdateTime"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -1865,7 +1875,7 @@
 </histogram>
 
 <histogram name="Blink.Render.DisplayLockActivationReason"
-    enum="DisplayLockActivationReason" expires_after="2021-05-23">
+    enum="DisplayLockActivationReason" expires_after="2021-08-09">
   <owner>vmpstr@chromium.org</owner>
   <owner>chrishtr@chromium.org</owner>
   <summary>This indicates the reason for display-locking activation</summary>
@@ -2037,7 +2047,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.BackendAvailability"
-    enum="WebOTPBackendAvailability" expires_after="M92">
+    enum="WebOTPBackendAvailability" expires_after="2021-08-09">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
   <summary>
@@ -2047,7 +2057,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.PendingOriginCount" units="origins"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
   <summary>
@@ -2058,7 +2068,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.DestroyedReason"
-    enum="WebOTPServiceDestroyedReason" expires_after="M92">
+    enum="WebOTPServiceDestroyedReason" expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2069,7 +2079,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.Infobar" enum="WebOTPServiceInfobarAction"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2081,7 +2091,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.Outcome" enum="WebOTPServiceOutcome"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2090,7 +2100,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.SmsParsingStatus" enum="SmsParsingStatus"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
   <summary>
@@ -2125,7 +2135,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeCancelOnSuccess" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2137,7 +2147,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeContinueOnSuccess" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2149,7 +2159,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeSmsReceive" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2160,7 +2170,8 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Sms.Receive.TimeSuccess" units="ms" expires_after="M92">
+<histogram name="Blink.Sms.Receive.TimeSuccess" units="ms"
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -2173,7 +2184,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeUserCancel" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>goto@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
@@ -2316,7 +2327,7 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.DocumentPolicy.Header"
-    enum="DocumentPolicyFeature" expires_after="2021-05-30">
+    enum="DocumentPolicyFeature" expires_after="2021-08-09">
   <owner>iclelland@chromium.org</owner>
   <owner>feature-control@chromium.org</owner>
   <summary>
@@ -2345,7 +2356,7 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.Extensions.Features" enum="FeatureObserver"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>chasej@chromium.org</owner>
   <owner>feature-control@chromium.org</owner>
   <summary>
@@ -2635,7 +2646,7 @@
 </histogram>
 
 <histogram name="BlinkGC.MainThreadMarkingThroughput" units="MB/s"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>mlippautz@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
@@ -2674,7 +2685,8 @@
   </summary>
 </histogram>
 
-<histogram name="BlinkGC.TimeForAtomicPhase" units="ms" expires_after="M92">
+<histogram name="BlinkGC.TimeForAtomicPhase" units="ms"
+    expires_after="2021-08-09">
   <owner>mlippautz@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
@@ -2696,7 +2708,8 @@
   </summary>
 </histogram>
 
-<histogram name="BlinkGC.TimeForCompleteSweep" units="ms" expires_after="M92">
+<histogram name="BlinkGC.TimeForCompleteSweep" units="ms"
+    expires_after="2021-08-09">
   <owner>bikineev@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
@@ -2753,7 +2766,7 @@
   </summary>
 </histogram>
 
-<histogram name="BlinkGC.TimeForMarking" units="ms" expires_after="M92">
+<histogram name="BlinkGC.TimeForMarking" units="ms" expires_after="2021-08-09">
   <owner>omerkatz@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
@@ -2763,7 +2776,8 @@
   </summary>
 </histogram>
 
-<histogram name="BlinkGC.TimeForMarkingRoots" units="ms" expires_after="M92">
+<histogram name="BlinkGC.TimeForMarkingRoots" units="ms"
+    expires_after="2021-08-09">
   <owner>omerkatz@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
@@ -2793,7 +2807,7 @@
 </histogram>
 
 <histogram name="BlinkGC.TimeForSweepingForeground" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>bikineev@chromium.org</owner>
   <owner>oilpan-reviews@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/browser/histograms.xml b/tools/metrics/histograms/histograms_xml/browser/histograms.xml
index de8c740..fedf14e 100644
--- a/tools/metrics/histograms/histograms_xml/browser/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/browser/histograms.xml
@@ -21,6 +21,15 @@
 
 <histograms>
 
+<histogram name="Browser.AnyWindowHasName" enum="Boolean"
+    expires_after="2021-06-30">
+  <owner>ellyjones@chromium.org</owner>
+  <summary>
+    Whether any browser window in the current session has a user-set name.
+    Logged once every histogram recording.
+  </summary>
+</histogram>
+
 <histogram name="Browser.BitmapFetcher.Decode" units="ms"
     expires_after="2021-06-27">
   <owner>manukh@chromium.org</owner>
@@ -290,7 +299,7 @@
 </histogram>
 
 <histogram name="Browser.Responsiveness.IOJanksTotalPerMinute" units="janks"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>gab@chromium.org</owner>
   <owner>olivierli@chromium.org</owner>
   <summary>
@@ -304,7 +313,7 @@
 </histogram>
 
 <histogram name="Browser.Responsiveness.IOJankyIntervalsPerMinute"
-    units="janks" expires_after="2021-06-06">
+    units="janks" expires_after="2021-08-09">
   <owner>gab@chromium.org</owner>
   <owner>olivierli@chromium.org</owner>
   <summary>
@@ -421,7 +430,7 @@
 </histogram>
 
 <histogram base="true" name="Browser.Tabs.TotalIncompleteSwitchDuration"
-    units="ms" expires_after="2021-04-25">
+    units="ms" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="TabSwitchingType" -->
 
   <owner>fdoray@chromium.org</owner>
@@ -469,7 +478,7 @@
 </histogram>
 
 <histogram name="BrowserDialogs.ExternalProtocol.HandleState"
-    enum="HandleStateType" expires_after="2021-05-30">
+    enum="HandleStateType" expires_after="2021-08-09">
   <owner>dominickn@chromium.org</owner>
   <owner>meacer@chromium.org</owner>
   <summary>
@@ -650,7 +659,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationResult"
-    enum="BrowserServicesVerificationResult" expires_after="M92">
+    enum="BrowserServicesVerificationResult" expires_after="2021-08-09">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -759,7 +768,7 @@
 </histogram>
 
 <histogram name="BrowserSwitcher.ExternalSitelistSize" units="rules"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>nicolaso@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -830,7 +839,7 @@
 </histogram>
 
 <histogram name="BrowserSwitcher.UrlListWildcard" enum="BooleanPresent"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>nicolaso@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/chrome/histograms.xml b/tools/metrics/histograms/histograms_xml/chrome/histograms.xml
index 1ec29fb8a..d0b2fdda 100644
--- a/tools/metrics/histograms/histograms_xml/chrome/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/chrome/histograms.xml
@@ -145,7 +145,7 @@
 </histogram>
 
 <histogram name="Chrome.Tabs.AnimationSmoothness.TabLoading" units="%"
-    expires_after="2021-06-04">
+    expires_after="2021-08-09">
   <owner>yichenz@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml b/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
index b297543..3bc82d6 100644
--- a/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Apps.NumberOfAppsForNotification"
-    enum="BooleanMultipleApps" expires_after="2021-06-06">
+    enum="BooleanMultipleApps" expires_after="2021-08-09">
   <owner>dominickn@chromium.org</owner>
   <owner>nancylingwang@chromium.org</owner>
   <summary>
@@ -182,7 +182,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Camera.OpenDeviceClientType"
-    enum="ChromeOSCameraClientType" expires_after="2021-05-16">
+    enum="ChromeOSCameraClientType" expires_after="2021-08-09">
   <owner>lnishan@chromium.org</owner>
   <owner>chromeos-camera-eng@google.com</owner>
   <summary>
@@ -292,7 +292,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CWP.ParseCPUFrequencies"
-    enum="ChromeOSParseCPUFrequencyStatus" expires_after="2021-04-18">
+    enum="ChromeOSParseCPUFrequencyStatus" expires_after="2021-08-09">
   <owner>gmx@chromium.org</owner>
   <owner>cwp-team@google.com</owner>
   <summary>
@@ -499,7 +499,7 @@
 </histogram>
 
 <histogram name="ChromeOS.KeyPermissionsManager.Migration"
-    enum="KeyPermissionsManagerMigrationStatus" expires_after="2021-06-06">
+    enum="KeyPermissionsManagerMigrationStatus" expires_after="2021-08-09">
   <owner>omorsi@google.com</owner>
   <owner>pmarko@google.com</owner>
   <summary>
@@ -694,7 +694,7 @@
 </histogram>
 
 <histogram name="ChromeOS.PrivacyScreen.Toggled"
-    enum="PrivacyScreenToggleUISurface" expires_after="2021-04-14">
+    enum="PrivacyScreenToggleUISurface" expires_after="2021-08-09">
   <owner>tengs@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -991,7 +991,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.PathVisited" enum="WebUISettingsPathHashes"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-customization@google.com</owner>
   <summary>
@@ -1155,7 +1155,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Sync.PreferencesMigrated" enum="BooleanMigrated"
-    expires_after="2021-03-28">
+    expires_after="2021-08-09">
   <owner>jamescook@chromium.org</owner>
   <owner>cros-customization@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/compositing/histograms.xml b/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
index 8f2d429..ee94fb5f 100644
--- a/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
@@ -83,7 +83,7 @@
 </histogram>
 
 <histogram name="Compositing.Browser.NumActivePictureLayers" units="layers"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -154,7 +154,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.ExtraDamage" units="%"
-    expires_after="2021-04-11">
+    expires_after="2021-08-09">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -183,7 +183,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.RootDamage" units="%"
-    expires_after="2021-04-25">
+    expires_after="2021-08-09">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -195,7 +195,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.PartialSwap.TotalDamage" units="%"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -207,7 +207,7 @@
 </histogram>
 
 <histogram name="Compositing.DirectRenderer.Software.DrawFrameUs"
-    units="microseconds" expires_after="2021-05-16">
+    units="microseconds" expires_after="2021-08-09">
   <owner>weiliangc@chromium.org</owner>
   <summary>
     Time spent drawing of composited layers by SoftwareRenderer, in
@@ -456,7 +456,7 @@
 </histogram>
 
 <histogram name="Compositing.Renderer.LCDTextDisallowedReasonKPixels"
-    enum="LCDTextDisallowedReason" expires_after="2021-06-06">
+    enum="LCDTextDisallowedReason" expires_after="2021-08-09">
   <owner>wangxianzhu@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -469,7 +469,7 @@
 </histogram>
 
 <histogram name="Compositing.Renderer.LCDTextDisallowedReasonLayers"
-    enum="LCDTextDisallowedReason" expires_after="2021-04-01">
+    enum="LCDTextDisallowedReason" expires_after="2021-08-09">
   <owner>wangxianzhu@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -500,7 +500,7 @@
 </histogram>
 
 <histogram name="Compositing.Renderer.NumRenderSurfaces" units="surfaces"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/content/histograms.xml b/tools/metrics/histograms/histograms_xml/content/histograms.xml
index 0590266..0b88990 100644
--- a/tools/metrics/histograms/histograms_xml/content/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/content/histograms.xml
@@ -781,7 +781,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ImageFetchStatus"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2021-06-01">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2021-08-09">
   <owner>harringtond@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/cookie/histograms.xml b/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
index 84dbb6c..1676457 100644
--- a/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
@@ -601,7 +601,7 @@
 </histogram>
 
 <histogram name="Cookie.TimeBlockedOnLoad" units="ms"
-    expires_after="2021-04-25">
+    expires_after="2021-08-09">
   <owner>nharper@chromium.org</owner>
   <summary>
     The amount of time (ms) between the cookie store load starting and
diff --git a/tools/metrics/histograms/histograms_xml/cros/histograms.xml b/tools/metrics/histograms/histograms_xml/cros/histograms.xml
index 09972de..050eecaa 100644
--- a/tools/metrics/histograms/histograms_xml/cros/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/cros/histograms.xml
@@ -34,7 +34,7 @@
 </histogram>
 
 <histogram name="CrosDisks.ArchiveType" enum="CrosDisksArchiveType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>fdegros@chromium.org</owner>
   <owner>chromeos-files-app@google.com</owner>
   <summary>
@@ -44,7 +44,7 @@
 </histogram>
 
 <histogram name="CrosDisks.DeviceMediaType" enum="CrosDisksDeviceMediaType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>fdegros@chromium.org</owner>
   <owner>chromeos-files-app@google.com</owner>
   <summary>
@@ -54,7 +54,7 @@
 </histogram>
 
 <histogram name="CrosDisks.FilesystemType" enum="CrosDisksFilesystemType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>fdegros@chromium.org</owner>
   <owner>chromeos-files-app@google.com</owner>
   <summary>
@@ -74,7 +74,7 @@
 </histogram>
 
 <histogram name="CrosDisks.Fuse.Rar2fs" enum="Rar2fsError"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>fdegros@chromium.org</owner>
   <owner>chromeos-files-app@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/crypt/histograms.xml b/tools/metrics/histograms/histograms_xml/crypt/histograms.xml
index 626966ff1..b1c233a 100644
--- a/tools/metrics/histograms/histograms_xml/crypt/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/crypt/histograms.xml
@@ -43,7 +43,7 @@
 </histogram>
 
 <histogram name="CryptAuth.ClientAppMetadataInstanceIdTokenFetch.Result"
-    enum="InstanceIDResult" expires_after="2021-06-06">
+    enum="InstanceIDResult" expires_after="2021-08-09">
   <owner>khorimoto@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="CryptAuth.ClientAppMetadataInstanceIdTokenFetch.Retries"
-    units="retries" expires_after="2021-06-06">
+    units="retries" expires_after="2021-08-09">
   <owner>khorimoto@chromium.org</owner>
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
@@ -128,7 +128,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncSoftwareFeaturesResult"
-    enum="BooleanSuccess" expires_after="2021-06-06">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -264,7 +264,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.DeviceSyncer.IsLocalDeviceMetadataConsistent"
-    enum="BooleanConsistent" expires_after="2021-06-06">
+    enum="BooleanConsistent" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -275,7 +275,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataDecryptionSuccess"
-    enum="BooleanSuccess" expires_after="2021-06-06">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -286,7 +286,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataParsingSuccess"
-    enum="BooleanSuccess" expires_after="2021-06-06">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -299,7 +299,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.ApiCallResult.GetFeatureStatuses"
-    enum="CryptAuthApiCallResult" expires_after="2021-06-06">
+    enum="CryptAuthApiCallResult" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -311,7 +311,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.CorrectNumberOfDevicesInResponse"
-    enum="BooleanExpected" expires_after="2021-06-06">
+    enum="BooleanExpected" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -335,7 +335,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsDuplicateDeviceId"
-    enum="BooleanDuplicate" expires_after="2021-06-06">
+    enum="BooleanDuplicate" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -347,7 +347,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsKnownFeatureType"
-    enum="BooleanKnown" expires_after="2021-06-06">
+    enum="BooleanKnown" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -361,7 +361,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsUnsupportedFeatureMarkedEnabled"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -374,7 +374,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.WasDeviceInResponseRequested"
-    enum="BooleanRequested" expires_after="2021-06-06">
+    enum="BooleanRequested" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -437,7 +437,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.EncryptionSuccess"
-    enum="BooleanSuccess" expires_after="2021-06-06">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -474,7 +474,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.IsEncryptingKeyEmpty"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -487,7 +487,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.InvocationReason"
-    enum="CryptAuthV2InvocationReason" expires_after="2021-06-06">
+    enum="CryptAuthV2InvocationReason" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -594,7 +594,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.MetadataSyncer.IsDeviceMetadataPacketValid"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -605,7 +605,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.IsDuplicateDeviceId"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -617,7 +617,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.NumV1Devices"
-    units="count" expires_after="2021-06-06">
+    units="count" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -629,7 +629,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.NumV2Devices"
-    units="count" expires_after="2021-06-06">
+    units="count" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -642,7 +642,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.PercentageOfV1DevicesReplacedByV2Devices"
-    units="%" expires_after="2021-06-06">
+    units="%" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -671,7 +671,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.RatioOfV2ToV1Devices"
-    units="%" expires_after="2021-06-06">
+    units="%" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -699,7 +699,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.Result.ResultCode"
-    enum="CryptAuthV2DeviceSyncResultCode" expires_after="2021-06-06">
+    enum="CryptAuthV2DeviceSyncResultCode" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -709,7 +709,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.Result.ResultType"
-    enum="CryptAuthV2DeviceSyncResultType" expires_after="2021-06-06">
+    enum="CryptAuthV2DeviceSyncResultType" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -794,7 +794,7 @@
 </histogram>
 
 <histogram name="CryptAuth.EnrollmentV2.InvocationReason"
-    enum="CryptAuthV2InvocationReason" expires_after="2021-06-06">
+    enum="CryptAuthV2InvocationReason" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -804,7 +804,7 @@
 </histogram>
 
 <histogram name="CryptAuth.EnrollmentV2.Result.ResultCode"
-    enum="CryptAuthV2EnrollmentResult" expires_after="2021-06-06">
+    enum="CryptAuthV2EnrollmentResult" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -824,7 +824,7 @@
 </histogram>
 
 <histogram name="CryptAuth.EnrollmentV2.UserKeyPairState"
-    enum="CryptAuthV2EnrollmentUserKeyPairState" expires_after="2021-06-06">
+    enum="CryptAuthV2EnrollmentUserKeyPairState" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -908,7 +908,7 @@
 </histogram>
 
 <histogram name="CryptAuth.Gcm.Registration.AttemptTimeWithRetries" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -940,7 +940,7 @@
 </histogram>
 
 <histogram name="CryptAuth.InstanceId.DidInstanceIdChange"
-    enum="BooleanChanged" expires_after="2021-06-06">
+    enum="BooleanChanged" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -951,7 +951,7 @@
 </histogram>
 
 <histogram name="CryptAuth.InstanceId.DidInstanceIdTokenChange"
-    enum="BooleanChanged" expires_after="2021-06-06">
+    enum="BooleanChanged" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml b/tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml
index db226c1b..d9df5cc5 100644
--- a/tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="Cryptohome.ChecksumStatus" enum="CryptohomeChecksumStatus"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>apronin@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -63,7 +63,7 @@
 </histogram>
 
 <histogram name="Cryptohome.DeprecatedApiCalled"
-    enum="CryptohomeDeprecatedApiCalled" expires_after="2021-06-06">
+    enum="CryptohomeDeprecatedApiCalled" expires_after="2021-08-09">
   <owner>apronin@chromium.org</owner>
   <owner>louiscollard@chromium.org</owner>
   <owner>zuan@chromium.org</owner>
@@ -371,7 +371,7 @@
 </histogram>
 
 <histogram name="Cryptohome.OOPMountCleanupResult"
-    enum="CryptohomeOOPMountCleanupResult" expires_after="2021-06-06">
+    enum="CryptohomeOOPMountCleanupResult" expires_after="2021-08-09">
   <owner>betuls@chromium.org</owner>
   <owner>jorgelo@chromium.org</owner>
   <summary>
@@ -381,7 +381,7 @@
 </histogram>
 
 <histogram name="Cryptohome.OOPMountOperationResult"
-    enum="CryptohomeOOPMountOperationResult" expires_after="2021-06-06">
+    enum="CryptohomeOOPMountOperationResult" expires_after="2021-08-09">
   <owner>betuls@chromium.org</owner>
   <owner>jorgelo@chromium.org</owner>
   <summary>
@@ -413,7 +413,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeSessionUnlock" units="ms"
-    expires_after="2021-04-11">
+    expires_after="2021-08-09">
   <owner>kerrnel@chromium.org</owner>
   <owner>mnissler@chromium.org</owner>
   <summary>
@@ -456,7 +456,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToMountEx" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jorgelo@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -476,7 +476,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToMountGuestEx" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jorgelo@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -486,7 +486,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToPerformEphemeralMount" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jorgelo@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -520,7 +520,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToPerformOOPMountOperation" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jorgelo@chromium.org</owner>
   <owner>betuls@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml b/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
index 990af8e..d462df8 100644
--- a/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
@@ -64,7 +64,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ConnectionStatusOnReturn.GSA"
-    enum="CustomTabsConnection" expires_after="M92">
+    enum="CustomTabsConnection" expires_after="2021-08-09">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -77,7 +77,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ConnectionStatusOnReturn.NonGSA"
-    enum="CustomTabsConnection" expires_after="M92">
+    enum="CustomTabsConnection" expires_after="2021-08-09">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -165,7 +165,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ParallelRequestStatusOnStart"
-    enum="CustomTabsParallelRequestStatusOnStart" expires_after="M92">
+    enum="CustomTabsParallelRequestStatusOnStart" expires_after="2021-08-09">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml b/tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml
index dc3ea3e..952a2a8 100644
--- a/tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml
@@ -256,7 +256,7 @@
 </histogram>
 
 <histogram name="DataReductionProxy.EnabledState"
-    enum="DataReductionProxyEnabledState" expires_after="2021-06-06">
+    enum="DataReductionProxyEnabledState" expires_after="2021-08-09">
   <owner>rajendrant@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -470,7 +470,7 @@
 </histogram>
 
 <histogram name="DataReductionProxy.UserViewedOriginalSize" units="KB"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>rajendrant@chromium.org</owner>
   <owner>robertogden@chromium.org</owner>
   <owner>src/components/data_reduction_proxy/OWNERS</owner>
@@ -495,7 +495,7 @@
 </histogram>
 
 <histogram name="DataReductionProxy.UserViewedSavingsSize" units="KB"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>rajendrant@chromium.org</owner>
   <owner>robertogden@chromium.org</owner>
   <owner>src/components/data_reduction_proxy/OWNERS</owner>
diff --git a/tools/metrics/histograms/histograms_xml/dev/histograms.xml b/tools/metrics/histograms/histograms_xml/dev/histograms.xml
index 80b39c37..9b568375 100644
--- a/tools/metrics/histograms/histograms_xml/dev/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/dev/histograms.xml
@@ -65,7 +65,7 @@
 </histogram>
 
 <histogram name="DevTools.BackgroundService.StartRecording"
-    enum="DevToolsBackgroundService" expires_after="M92">
+    enum="DevToolsBackgroundService" expires_after="2021-08-09">
   <owner>yangguo@chromium.org</owner>
   <owner>bmeurer@chromium.org</owner>
   <owner>rayankans@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/download/histograms.xml b/tools/metrics/histograms/histograms_xml/download/histograms.xml
index 84b2d9b..1d14c21 100644
--- a/tools/metrics/histograms/histograms_xml/download/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/download/histograms.xml
@@ -48,7 +48,7 @@
 </histogram>
 
 <histogram name="Download.CancelReason" enum="DownloadCancelReason"
-    expires_after="2021-04-01">
+    expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>Records why the download is canceled.</summary>
@@ -563,7 +563,7 @@
 </histogram>
 
 <histogram name="Download.Parallelizable.FileSize" units="KB"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>The download size of a parallelizable download.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
index 732eb02..91c8b7f2 100644
--- a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="Enterprise.AppRestrictionLoadTime2.EmptyBundle" units="ms"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>twellington@google.com</owner>
   <owner>tedchcoc@chromium.org</owner>
   <summary>
@@ -52,7 +52,7 @@
 </histogram>
 
 <histogram name="Enterprise.AppRestrictionLoadTime2.NonEmptyBundle" units="ms"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>twellington@google.com</owner>
   <owner>tedchcoc@chromium.org</owner>
   <summary>
@@ -162,7 +162,7 @@
 </histogram>
 
 <histogram base="true" name="Enterprise.AutoEnrollmentRequestStatus"
-    enum="EnterpriseDeviceManagementStatus" expires_after="2021-05-02">
+    enum="EnterpriseDeviceManagementStatus" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="EnterpriseAutoEnrollmentType". -->
 
   <owner>pmarko@chromium.org</owner>
@@ -170,6 +170,29 @@
   <summary>URL fetcher status for auto-enrollment requests.</summary>
 </histogram>
 
+<histogram base="true" name="Enterprise.BrowserSigninIOS.SignedOutByPolicy"
+    enum="BooleanHit" expires_after="2021-06-30">
+  <owner>gujen@google.com</owner>
+  <owner>goanuj@google.com</owner>
+  <summary>
+    Recorded when a signed-in user is automatically signed out of the browser
+    due to the BrowserSignin policy being set to 0 (sign-in disabled) by their
+    organization. The sample is always recorded as true.
+  </summary>
+</histogram>
+
+<histogram base="true"
+    name="Enterprise.BrowserSigninIOS.SignInInterruptedByPolicy"
+    enum="BooleanHit" expires_after="2021-06-30">
+  <owner>gujen@google.com</owner>
+  <owner>goanuj@google.com</owner>
+  <summary>
+    Recorded when a user is in the process of signing in to the browser, but is
+    interrupted due to the BrowserSignin policy being set to 0 (sign-in
+    disabled) by their organization. The sample is always recorded as true.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Enterprise.CachedDevicePolicyDeviceIdValidity"
     enum="EnterprisePolicyDeviceIdValidity" expires_after="M95">
   <owner>emaxx@chromium.org</owner>
@@ -494,7 +517,7 @@
 </histogram>
 
 <histogram name="Enterprise.DMServerRequestSuccess"
-    enum="EnterpriseDMServerRequestSuccess" expires_after="2021-06-06">
+    enum="EnterpriseDMServerRequestSuccess" expires_after="2021-08-09">
   <owner>poromov@chromium.org</owner>
   <owner>managed-devices@google.com</owner>
   <summary>
@@ -641,7 +664,7 @@
 </histogram>
 
 <histogram name="Enterprise.EnrollmentTime.Success" units="ms"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>raleksandrov@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -885,7 +908,7 @@
 
 <histogram
     name="Enterprise.MachineLevelUserCloudPolicyEnrollment.RequestFailureTime"
-    units="ms" expires_after="2021-06-01">
+    units="ms" expires_after="2021-08-09">
   <owner>rogerta@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -965,7 +988,7 @@
 </histogram>
 
 <histogram name="Enterprise.Policies" enum="EnterprisePolicies"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mnissler@chromium.org</owner>
   <summary>
     A set of enterprise policy rules that are in use. This is recorded every 24
@@ -1136,7 +1159,7 @@
 </histogram>
 
 <histogram name="Enterprise.RegularUserSession.SessionLength" units="minutes"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>marcgrimme@chromium.org</owner>
   <owner>managed-devices@google.com</owner>
   <summary>
@@ -1194,7 +1217,7 @@
 </histogram>
 
 <histogram name="Enterprise.StorePolicy.Duration" units="ms"
-    expires_after="2021-05-20">
+    expires_after="2021-08-09">
   <owner>mpolzer@google.com</owner>
   <owner>managed-platforms@google.com</owner>
   <summary>
@@ -1580,7 +1603,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.InvalidPolicies" enum="EnterprisePolicies"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>pastarmovj@chromium.org</owner>
   <summary>
     A set of policy rules that were ignored due to integrity violations while
@@ -1590,7 +1613,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.InvalidPoliciesDetected"
-    units="disabled policies" expires_after="2021-05-05">
+    units="disabled policies" expires_after="2021-08-09">
   <owner>pastarmovj@chromium.org</owner>
   <owner>zmin@chomium.org</owner>
   <summary>
@@ -1624,7 +1647,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsFullyManaged2" enum="IsFullyManagedBoolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>twellington@google.com</owner>
   <owner>tedchcoc@chromium.org</owner>
   <summary>
@@ -1681,7 +1704,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.Mac.IsCurrentUserDomainUser" enum="Boolean"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>avi@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -1701,7 +1724,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.Mac.IsDeviceMDMEnrolledNew"
-    enum="EnterpriseMacMDMStatusNew" expires_after="2021-05-23">
+    enum="EnterpriseMacMDMStatusNew" expires_after="2021-08-09">
   <owner>avi@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -1712,7 +1735,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.Mac.IsDeviceMDMEnrolledOld"
-    enum="EnterpriseMacMDMStatusOld" expires_after="2021-05-23">
+    enum="EnterpriseMacMDMStatusOld" expires_after="2021-08-09">
   <owner>avi@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/event/histograms.xml b/tools/metrics/histograms/histograms_xml/event/histograms.xml
index 03b2487e..3f43da0 100644
--- a/tools/metrics/histograms/histograms_xml/event/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/event/histograms.xml
@@ -22,7 +22,7 @@
 <histograms>
 
 <histogram name="Event.AggregatedLatency.Renderer2" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>flackr@chromium.org</owner>
   <summary>
     Time between initiation of any input event and the renderer receiving and
@@ -55,7 +55,7 @@
 </histogram>
 
 <histogram name="Event.AsyncTargeting.AsyncClientDepth" units="clients"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -65,7 +65,7 @@
 </histogram>
 
 <histogram name="Event.AsyncTargeting.ResponseTime" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
@@ -397,7 +397,7 @@
 </histogram>
 
 <histogram name="Event.Latency.OS_NO_VALIDATION.POSITIVE" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>sullivan@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -553,7 +553,7 @@
 
 <histogram
     name="Event.Latency.ScrollBegin.Scrollbar.TimeToScrollUpdateSwapBegin4"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>nzolghadr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>input-dev@chromium.org</owner>
@@ -657,7 +657,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.Touch.GpuSwap2" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>flackr@chromium.org</owner>
   <summary>
     Time between gpu starts to swap the first ScrollUpdate gesture event in a
@@ -1053,7 +1053,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.TimeToScrollUpdateSwapBegin2"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>nzolghadr@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel/touch event and start of the frame
@@ -1224,7 +1224,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Touch.GpuSwap2"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>flackr@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -1437,7 +1437,7 @@
 </histogram>
 
 <histogram name="Event.PassiveListeners" enum="EventResultType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>dtapuska@chromium.org</owner>
   <summary>
     The result of handling of MouseWheel, TouchStart, TouchMove, TouchEnd events
@@ -1637,7 +1637,7 @@
 </histogram>
 
 <histogram name="Event.VizHitTest.AsyncHitTestReasons"
-    enum="AsyncHitTestReasons" expires_after="2021-06-06">
+    enum="AsyncHitTestReasons" expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/extension/histograms.xml b/tools/metrics/histograms/histograms_xml/extension/histograms.xml
index 36f8293..e31cda2 100644
--- a/tools/metrics/histograms/histograms_xml/extension/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/extension/histograms.xml
@@ -177,7 +177,7 @@
 </histogram>
 
 <histogram name="ExtensionOverrideBubble.SettingsApiUserSelectionHomePage"
-    enum="ExtensionBubbleAction" expires_after="2021-04-01">
+    enum="ExtensionBubbleAction" expires_after="2021-08-09">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
index b949a206..a884b34 100644
--- a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
@@ -264,7 +264,7 @@
 </histogram>
 
 <histogram name="Extensions.BookmarkAppLaunchSource" enum="AppLaunchSource"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>benwells@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <owner>mgiuca@chromium.org</owner>
@@ -359,7 +359,7 @@
 </histogram>
 
 <histogram name="Extensions.ContentVerification.ComputeHashesOnInstallResult"
-    enum="BooleanSuccess" expires_after="2021-05-02">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>burunduk@chromium.org</owner>
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -375,7 +375,7 @@
 </histogram>
 
 <histogram name="Extensions.ContentVerification.ComputeHashesOnInstallTime"
-    units="ms" expires_after="2021-05-02">
+    units="ms" expires_after="2021-08-09">
   <owner>burunduk@chromium.org</owner>
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -403,7 +403,7 @@
 </histogram>
 
 <histogram name="Extensions.ContentVerification.FetchResult"
-    enum="BooleanSuccess" expires_after="2021-05-30">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
@@ -521,7 +521,7 @@
 </histogram>
 
 <histogram name="Extensions.CorruptPolicyExtensionResolved" units="ms"
-    expires_after="2021-03-28">
+    expires_after="2021-08-09">
   <owner>burunduk@chromium.org</owner>
   <owner>lazyboy@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
@@ -595,7 +595,7 @@
 
 <histogram
     name="Extensions.DeclarativeNetRequest.EvaluateBeforeRequestTime.SingleExtension2"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>karandeepb@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -608,7 +608,7 @@
 
 <histogram
     name="Extensions.DeclarativeNetRequest.EvaluateRequestTime.AllExtensions3"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>karandeepb@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -961,7 +961,7 @@
 </histogram>
 
 <histogram name="Extensions.DisableReason" enum="ExtensionDisableReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -1127,7 +1127,7 @@
 </histogram>
 
 <histogram name="Extensions.ExtensionAddDisabledRemotelyReason"
-    enum="ExtensionUpdateCheckDataKey" expires_after="2021-06-06">
+    enum="ExtensionUpdateCheckDataKey" expires_after="2021-08-09">
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1160,7 +1160,7 @@
 </histogram>
 
 <histogram name="Extensions.ExtensionDisabledRemotely"
-    enum="ExtensionUpdateCheckDataKey" expires_after="2021-06-06">
+    enum="ExtensionUpdateCheckDataKey" expires_after="2021-08-09">
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1429,7 +1429,7 @@
 </histogram>
 
 <histogram name="Extensions.ForceInstalledFailureNoUpdatesInfo"
-    enum="ExtensionNoUpdatesInfo" expires_after="2021-06-06">
+    enum="ExtensionNoUpdatesInfo" expires_after="2021-08-09">
   <owner>snijhara@google.com</owner>
   <owner>swapnilgupta@google.com</owner>
   <owner>burunduk@chromium.org</owner>
@@ -1801,7 +1801,7 @@
 </histogram>
 
 <histogram name="Extensions.Functions.ExtensionServiceWorkerCalls"
-    enum="ExtensionFunctions" expires_after="2021-04-11">
+    enum="ExtensionFunctions" expires_after="2021-08-09">
   <owner>lazyboy@chromium.org</owner>
   <owner>dbertoni@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -2295,7 +2295,7 @@
 </histogram>
 
 <histogram name="Extensions.InstallPrompt.Type2"
-    enum="ExtensionInstallPromptType" expires_after="2021-06-01">
+    enum="ExtensionInstallPromptType" expires_after="2021-08-09">
   <owner>meacer@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
@@ -2823,7 +2823,7 @@
 </histogram>
 
 <histogram name="Extensions.NotAllowlistedEnabled" units="extensions"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>jeffcyr@google.com</owner>
   <owner>lucferron@chromium.org</owner>
   <summary>
@@ -2859,7 +2859,7 @@
 </histogram>
 
 <histogram name="Extensions.Permissions_Install3" enum="ExtensionPermission3"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -3278,7 +3278,7 @@
 </histogram>
 
 <histogram name="Extensions.StartupPagesOverrides" units="units"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>kelvinjiang@chromium.org</owner>
   <summary>
@@ -3659,7 +3659,7 @@
 </histogram>
 
 <histogram name="Extensions.WebRequestCount" units="extensions"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>karandeepb@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/file/histograms.xml b/tools/metrics/histograms/histograms_xml/file/histograms.xml
index 0a04f794b..6ea11159 100644
--- a/tools/metrics/histograms/histograms_xml/file/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/file/histograms.xml
@@ -133,14 +133,14 @@
 </histogram>
 
 <histogram name="FileBrowser.DriveHostedFilePinSuccess" enum="BooleanSuccess"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>austinct@chromium.org</owner>
   <owner>dats@chromium.org</owner>
   <summary>Tracks success rate of pinning hosted files in Drive.</summary>
 </histogram>
 
 <histogram name="FileBrowser.DrivePinSuccess" enum="BooleanSuccess"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>austinct@chromium.org</owner>
   <owner>dats@chromium.org</owner>
   <summary>Tracks success rate of pinning files in Drive.</summary>
@@ -197,7 +197,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FormatFileSystemType"
-    enum="FileManagerFormatFileSystemType" expires_after="2021-05-02">
+    enum="FileManagerFormatFileSystemType" expires_after="2021-08-09">
   <owner>austinct@chromium.org</owner>
   <summary>
     Chrome OS File Browser: this records the filesystem selected when formatting
@@ -490,7 +490,7 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.LoadTime" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>Chrome OS Photo Editor: time to load an image from a file.</summary>
@@ -661,7 +661,7 @@
 </histogram>
 
 <histogram name="FileBrowser.SuggestApps.Install"
-    enum="SuggestAppsDialogInstall" expires_after="M92">
+    enum="SuggestAppsDialogInstall" expires_after="2021-08-09">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -671,7 +671,7 @@
 </histogram>
 
 <histogram name="FileBrowser.SuggestApps.Load" enum="SuggestAppsDialogLoad"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml b/tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml
index adcb052..2c8b172a 100644
--- a/tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml
@@ -48,7 +48,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Reset.ResetContextMode"
-    enum="FingerprintSensorMode" expires_after="2021-06-06">
+    enum="FingerprintSensorMode" expires_after="2021-08-09">
   <owner>tomhughes@chromium.org</owner>
   <owner>chromeos-fingerprint@google.com</owner>
   <summary>The mode FPMCU was in when we reset context.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/gcm/histograms.xml b/tools/metrics/histograms/histograms_xml/gcm/histograms.xml
index 8bd3813..b165913c 100644
--- a/tools/metrics/histograms/histograms_xml/gcm/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/gcm/histograms.xml
@@ -391,7 +391,7 @@
 </histogram>
 
 <histogram name="GCM.SendWebPushMessageResult" enum="SendWebPushMessageResult"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -401,7 +401,7 @@
 </histogram>
 
 <histogram name="GCM.SendWebPushMessageStatusCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M92">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2021-08-09">
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/geolocation/histograms.xml b/tools/metrics/histograms/histograms_xml/geolocation/histograms.xml
index fe6519c..f00054a 100644
--- a/tools/metrics/histograms/histograms_xml/geolocation/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/geolocation/histograms.xml
@@ -22,7 +22,7 @@
 <histograms>
 
 <histogram name="Geolocation.AuthorizationActionExistingUser"
-    enum="GeolocationAuthorizationAction" expires_after="2021-06-06">
+    enum="GeolocationAuthorizationAction" expires_after="2021-08-09">
   <owner>mattreynolds@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>
@@ -32,7 +32,7 @@
 </histogram>
 
 <histogram name="Geolocation.AuthorizationActionNewUser"
-    enum="GeolocationAuthorizationAction" expires_after="2021-06-06">
+    enum="GeolocationAuthorizationAction" expires_after="2021-08-09">
   <owner>mattreynolds@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
index 6f033a0b7..44a238b7 100644
--- a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
@@ -785,7 +785,7 @@
 </histogram>
 
 <histogram name="GPU.GPUProcessTerminationStatus2" enum="GpuTerminationStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vmiura@chromium.org</owner>
   <summary>
     Counts for each time the GPU Process Host detects the process dies.
@@ -823,7 +823,7 @@
 </histogram>
 
 <histogram name="GPU.IntelGpuGeneration" enum="IntelGpuGeneration"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -1060,7 +1060,7 @@
 </histogram>
 
 <histogram name="GPU.Scheduler.RunTaskTime" units="microseconds"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>kylechar@chromium.org</owner>
   <owner>chrome-gpu-metrics@google.com</owner>
   <summary>
@@ -1090,7 +1090,7 @@
 </histogram>
 
 <histogram name="GPU.SharedImage.ContentConsumed" enum="BooleanMatched"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>penghuang@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -1152,7 +1152,7 @@
 </histogram>
 
 <histogram name="GPU.SwapTimeUs" units="microseconds"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>vasilyt@chromium.org</owner>
   <owner>backer@chromium.org</owner>
   <summary>
@@ -1172,7 +1172,7 @@
 </histogram>
 
 <histogram name="GPU.Vulkan.CreateExternalVkSemaphore" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
@@ -1194,7 +1194,7 @@
 </histogram>
 
 <histogram name="GPU.Vulkan.ImportSemaphoreGLPerSwapBuffers" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
@@ -1202,7 +1202,7 @@
 </histogram>
 
 <histogram name="GPU.Vulkan.ImportVkSemaphoreHandle" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
@@ -1213,7 +1213,7 @@
 </histogram>
 
 <histogram name="GPU.Vulkan.ImportVkSemaphoreIntoGL" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
@@ -1283,7 +1283,7 @@
 </histogram>
 
 <histogram name="GPU.Vulkan.QueueSubmitPerSwapBuffers" units="units"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 219625f..ca07314b 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -6512,6 +6512,7 @@
 
 <histogram_suffixes name="FeedNetworkRequestType" separator=".">
   <suffix name="FeedQuery" label="Requests to fetch new feed content"/>
+  <suffix name="NextPage" label="Requests to fetch the next page of the feed"/>
   <suffix name="UploadActions" label="Requests to upload user action data"/>
   <affected-histogram name="ContentSuggestions.Feed.Network.ResponseStatus"/>
 </histogram_suffixes>
@@ -18241,7 +18242,11 @@
   <affected-histogram name="Startup.BrowserMessageLoopStartTimeFromMainEntry3"/>
   <affected-histogram name="Startup.BrowserOpenTabs"/>
   <affected-histogram name="Startup.BrowserWindow.FirstPaint"/>
-  <affected-histogram name="Startup.BrowserWindow.FirstPaint.CompositingEnded"/>
+  <affected-histogram name="Startup.BrowserWindow.FirstPaint.CompositingEnded">
+    <obsolete>
+      Obsolete as of Feb 2021.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram name="Startup.BrowserWindowDisplay"/>
   <affected-histogram name="Startup.FirstWebContents.MainFrameLoad"/>
   <affected-histogram name="Startup.FirstWebContents.MainFrameLoad2"/>
diff --git a/tools/metrics/histograms/histograms_xml/history/histograms.xml b/tools/metrics/histograms/histograms_xml/history/histograms.xml
index 8f75e4f..9f8713e 100644
--- a/tools/metrics/histograms/histograms_xml/history/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/history/histograms.xml
@@ -487,7 +487,7 @@
 </histogram>
 
 <histogram name="History.DomainCountQueryTime" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -690,7 +690,7 @@
 </histogram>
 
 <histogram name="History.MonthlyHostCount" units="hosts"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mpearson@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <summary>
@@ -857,7 +857,7 @@
 </histogram>
 
 <histogram name="History.WeeklyHostCount" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mpearson@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/image/histograms.xml b/tools/metrics/histograms/histograms_xml/image/histograms.xml
index c96f2dfe..7a451cacda 100644
--- a/tools/metrics/histograms/histograms_xml/image/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/image/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.ClientResult"
-    enum="ImageAnnotationServiceClientResult" expires_after="2021-05-30">
+    enum="ImageAnnotationServiceClientResult" expires_after="2021-08-09">
   <owner>dmazzoni@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>martis@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/input/histograms.xml b/tools/metrics/histograms/histograms_xml/input/histograms.xml
index 923320ca..eaf2d38 100644
--- a/tools/metrics/histograms/histograms_xml/input/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/input/histograms.xml
@@ -230,14 +230,14 @@
 </histogram>
 
 <histogram name="InputMethod.AutoCorrectLevel" enum="IMECorrectionLevel"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>The auto-correction level for suggestion engine.</summary>
 </histogram>
 
 <histogram name="InputMethod.Category" enum="InputMethodCategory"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -247,7 +247,7 @@
 </histogram>
 
 <histogram name="InputMethod.Commit.Index" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -337,7 +337,7 @@
 </histogram>
 
 <histogram name="InputMethod.Handwriting.CharsEdited5s" units="chars"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -401,7 +401,7 @@
 </histogram>
 
 <histogram name="InputMethod.KeyEventLatency" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>Time taken by the engine to handle a key event.</summary>
@@ -521,7 +521,7 @@
 </histogram>
 
 <histogram name="InputMethod.PkCommit.Index" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -649,7 +649,7 @@
 </histogram>
 
 <histogram name="InputMethod.VirtualKeyboard.ErrorType"
-    enum="VirtualKeyboardErrorTypeHashes" expires_after="2021-06-06">
+    enum="VirtualKeyboardErrorTypeHashes" expires_after="2021-08-09">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>Errors from the virtual keyboard extension</summary>
diff --git a/tools/metrics/histograms/histograms_xml/interstitial/histograms.xml b/tools/metrics/histograms/histograms_xml/interstitial/histograms.xml
index cc1f4ba..00162b17 100644
--- a/tools/metrics/histograms/histograms_xml/interstitial/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/interstitial/histograms.xml
@@ -263,7 +263,7 @@
 </histogram>
 
 <histogram name="TypedNavigationUpgradeThrottle.Event"
-    enum="TypedNavigationUpgradeThrottleEvent" expires_after="M92">
+    enum="TypedNavigationUpgradeThrottleEvent" expires_after="2021-08-09">
   <owner>meacer@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/ios/histograms.xml b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
index a8a21ea..a2fbbfa 100644
--- a/tools/metrics/histograms/histograms_xml/ios/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
@@ -882,7 +882,7 @@
 </histogram>
 
 <histogram name="IOS.Spotlight.Origin" enum="IOSSpotlightOrigin"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>olivierrobin@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/login/histograms.xml b/tools/metrics/histograms/histograms_xml/login/histograms.xml
index 81efc4d..ccb3597f 100644
--- a/tools/metrics/histograms/histograms_xml/login/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/login/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="Login.BrowserShutdownTime" units="ms"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>xiyuan@chromium.org</owner>
   <summary>
     Tracks the browser process shutdown time from when SIGTERM is sent to the
@@ -92,7 +92,7 @@
 </histogram>
 
 <histogram name="Login.MountNamespaceCreationSuccess" enum="Boolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>betuls@chromium.org</owner>
   <owner>jorgelo@chromium.org</owner>
   <owner>chromeos-security-core@google.com</owner>
@@ -158,7 +158,7 @@
 </histogram>
 
 <histogram name="Login.PasswordChangeFlow" enum="LoginPasswordChangeFlow"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>xiyuan@chromium.org</owner>
   <owner>omrilio@chromium.org</owner>
   <summary>
@@ -238,7 +238,7 @@
 </histogram>
 
 <histogram name="Login.TokenCheckResponseTime" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>rsorokin@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -280,7 +280,7 @@
 </histogram>
 
 <histogram name="Login.UsersActiveWeekly" units="users"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>alemate@chromium.org</owner>
   <owner>achuith@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/media/histograms.xml b/tools/metrics/histograms/histograms_xml/media/histograms.xml
index 1403f90..1db38861 100644
--- a/tools/metrics/histograms/histograms_xml/media/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/media/histograms.xml
@@ -296,7 +296,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.StreamCallbackError2"
-    enum="AudioCaptureDeviceError" expires_after="2021-04-25">
+    enum="AudioCaptureDeviceError" expires_after="2021-08-09">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -493,7 +493,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.TotalDelayMs" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>armax@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <summary>
@@ -677,7 +677,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.SinkCache.UsedForSinkCreation"
-    enum="BooleanUsage" expires_after="2021-04-05">
+    enum="BooleanUsage" expires_after="2021-08-09">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -865,7 +865,7 @@
 </histogram>
 
 <histogram name="Media.AudioOutputController.CallbackError" enum="BooleanError"
-    expires_after="2021-04-05">
+    expires_after="2021-08-09">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -938,7 +938,7 @@
 
 <histogram name="Media.AudioOutputResampler.OpenLowLatencyStream"
     enum="AudioOutputResamplerLowLatencyOpenStreamResult"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>armax@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -1041,7 +1041,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererImpl.SinkStatus" enum="OutputDeviceStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>armax@chromium.org</owner>
   <owner>dalecurtis@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
@@ -1148,7 +1148,7 @@
 </histogram>
 
 <histogram name="Media.Capabilities.DecodingInfo.Time.Video.Clear" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>chcunningham@chromium.org</owner>
   <owner>mlamouri@google.com</owner>
   <owner>media-dev@chromium.org</owner>
@@ -1606,7 +1606,7 @@
 </histogram>
 
 <histogram name="Media.EME.EncryptedMediaEnabled" enum="BooleanEnabled"
-    expires_after="2021-05-07">
+    expires_after="2021-08-09">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -1907,7 +1907,7 @@
 </histogram>
 
 <histogram name="Media.Feeds.AggregateWatchtime" units="ms"
-    expires_after="2021-06-07">
+    expires_after="2021-08-09">
   <owner>beccahughes@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -1918,7 +1918,7 @@
 </histogram>
 
 <histogram name="Media.Feeds.Feed.ReadResult" enum="MediaFeedReadResult"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>beccahughes@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -2133,7 +2133,7 @@
 </histogram>
 
 <histogram name="Media.History.DatabaseSize" units="KB"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>beccahughes@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -2142,7 +2142,7 @@
 </histogram>
 
 <histogram name="Media.History.Init.Result" enum="MediaHistoryInitResult"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>beccahughes@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -2153,7 +2153,7 @@
 </histogram>
 
 <histogram name="Media.History.Init.ResultAfterDelete"
-    enum="MediaHistoryInitResult" expires_after="2021-05-09">
+    enum="MediaHistoryInitResult" expires_after="2021-08-09">
   <owner>beccahughes@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -2943,7 +2943,7 @@
 </histogram>
 
 <histogram name="Media.PipelineStatus.Start" enum="PipelineStatus"
-    expires_after="2021-05-07">
+    expires_after="2021-08-09">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3186,7 +3186,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.AverageQueueLengthX10"
-    units="frames" expires_after="2021-05-31">
+    units="frames" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3197,7 +3197,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.DrainedFramesPermille"
-    units="permille" expires_after="2021-05-31">
+    units="permille" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3208,7 +3208,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.DroppedFramesPermille"
-    units="permille" expires_after="2021-05-31">
+    units="permille" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3219,7 +3219,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.EnterDrainModeCount"
-    units="count" expires_after="2021-05-31">
+    units="count" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3240,7 +3240,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.NoNewFrameToRenderPermille"
-    units="permille" expires_after="2021-05-31">
+    units="permille" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3251,7 +3251,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.ReduceSteadyStateCount"
-    units="count" expires_after="2021-05-31">
+    units="count" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3262,7 +3262,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.TotalFrames" units="frames"
-    expires_after="2021-05-31">
+    expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3273,7 +3273,7 @@
 </histogram>
 
 <histogram name="Media.RtcLowLatencyVideoRenderer.TryToRenderFrameCount"
-    units="count" expires_after="2021-05-31">
+    units="count" expires_after="2021-08-09">
   <owner>kron@chromium.org</owner>
   <owner>webrtc-video@google.com</owner>
   <summary>
@@ -3845,7 +3845,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.DelayUntilFirstFrame" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>guidou@chromium.org</owner>
   <owner>armax@chromium.org</owner>
   <summary>
@@ -3856,7 +3856,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Device.SupportedPixelFormat"
-    enum="VideoPixelFormatUnion" expires_after="M92">
+    enum="VideoPixelFormatUnion" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -3868,7 +3868,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Device.SupportedResolution"
-    enum="VideoResolutionDesignation" expires_after="M92">
+    enum="VideoResolutionDesignation" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -3934,7 +3934,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Mac.Device.CapturedIOSurface"
-    enum="Boolean" expires_after="M92">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -3946,7 +3946,7 @@
 
 <histogram
     name="Media.VideoCapture.Mac.Device.CapturedWithRequestedPixelFormat"
-    enum="Boolean" expires_after="M92">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -3957,7 +3957,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Mac.Device.CapturedWithRequestedResolution"
-    enum="ResolutionComparison" expires_after="M92">
+    enum="ResolutionComparison" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -3969,7 +3969,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.Mac.Device.RequestedPixelFormat"
-    enum="VideoPixelFormatUnion" expires_after="M92">
+    enum="VideoPixelFormatUnion" expires_after="2021-08-09">
   <owner>eshr@google.com</owner>
   <owner>handellm@google.com</owner>
   <summary>
@@ -4683,7 +4683,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Cast.LaunchSessionResponse.AppType"
-    enum="MediaRouterResponseReceiverAppType" expires_after="2021-06-08">
+    enum="MediaRouterResponseReceiverAppType" expires_after="2021-08-09">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -4775,7 +4775,7 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Start.Failure"
-    enum="MirrorFailureType" expires_after="2021-06-06">
+    enum="MirrorFailureType" expires_after="2021-08-09">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/memory/histograms.xml b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
index 87db055..7c5677ab 100644
--- a/tools/metrics/histograms/histograms_xml/memory/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
@@ -1038,7 +1038,7 @@
 
 <histogram
     name="Memory.Experimental.Renderer.PeakResidentSet.AtHighestPrivateMemoryFootprint"
-    units="MB" expires_after="2021-06-06">
+    units="MB" expires_after="2021-08-09">
   <owner>tasak@google.com</owner>
   <owner>bartekn@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
index f16e046..33e840e0 100644
--- a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
@@ -173,7 +173,7 @@
 </histogram>
 
 <histogram base="true" name="Mobile.Messages.Badge.Tapped"
-    enum="MobileMessagesBadgeState" expires_after="2021-05-23">
+    enum="MobileMessagesBadgeState" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="Mobile.Messages.Type" -->
 
   <owner>sczs@chromium.org</owner>
@@ -182,7 +182,7 @@
 </histogram>
 
 <histogram base="true" name="Mobile.Messages.Banner.Dismiss"
-    enum="MobileMessagesBannerDismissType" expires_after="2021-05-23">
+    enum="MobileMessagesBannerDismissType" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="Mobile.Messages.Type" -->
 
   <owner>sczs@chromium.org</owner>
@@ -590,7 +590,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.Result"
-    enum="DownloadLocationDialogResult" expires_after="2021-06-06">
+    enum="DownloadLocationDialogResult" expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -624,7 +624,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="2021-05-02">
+    enum="DownloadLocationDirectoryType" expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
   <owner>qinmin@chromium.org</owner>
@@ -636,14 +636,14 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Download.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="2021-05-02">
+    enum="DownloadLocationDirectoryType" expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>Records the directory type when download is completed.</summary>
 </histogram>
 
 <histogram name="MobileDownload.Location.Setting.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="2021-05-02">
+    enum="DownloadLocationDirectoryType" expires_after="2021-08-09">
   <owner>xingliu@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>
@@ -1045,7 +1045,7 @@
 </histogram>
 
 <histogram name="MobileSignInPromo.BookmarkManager.ImpressionsTilXButton"
-    units="impressions" expires_after="2021-04-20">
+    units="impressions" expires_after="2021-08-09">
   <owner>jlebel@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/multi_device/histograms.xml b/tools/metrics/histograms/histograms_xml/multi_device/histograms.xml
index 9eb1aa0..6c3c5ee3 100644
--- a/tools/metrics/histograms/histograms_xml/multi_device/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/multi_device/histograms.xml
@@ -42,7 +42,7 @@
 <histogram
     name="MultiDevice.DeviceSyncService.FindEligibleDevices.Result.FailureReason"
     enum="MultiDevice_DeviceSyncService_DeviceSyncRequestFailureReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -61,7 +61,7 @@
 
 <histogram name="MultiDevice.DeviceSyncService.ForceSyncNow.Result"
     enum="MultiDevice_DeviceSyncService_ForceCryptAuthOperationResult"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>Result for when ForceSyncNow is called.</summary>
@@ -69,7 +69,7 @@
 
 <histogram
     name="MultiDevice.DeviceSyncService.SetSoftwareFeatureState.Disable.FailedFeature"
-    enum="MultiDevice_DeviceSyncService_Features" expires_after="2021-04-04">
+    enum="MultiDevice_DeviceSyncService_Features" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -79,7 +79,7 @@
 
 <histogram
     name="MultiDevice.DeviceSyncService.SetSoftwareFeatureState.Enable.FailedFeature"
-    enum="MultiDevice_DeviceSyncService_Features" expires_after="2021-04-04">
+    enum="MultiDevice_DeviceSyncService_Features" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>Breaks down which features failed when attempted to enable.</summary>
@@ -95,7 +95,7 @@
 <histogram
     name="MultiDevice.DeviceSyncService.SetSoftwareFeatureState.Result.FailureReason"
     enum="MultiDevice_DeviceSyncService_DeviceSyncRequestFailureReason"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -106,7 +106,7 @@
 
 <histogram name="MultiDevice.ForgetHostConfirmed"
     enum="MultiDevice_VerifyAndForgetHostConfirmationState"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -115,7 +115,7 @@
 </histogram>
 
 <histogram name="MultiDevice.PostOOBESetupFlow.PageShown"
-    enum="MultiDevice_PostOOBESetupFlow_Page" expires_after="2021-06-06">
+    enum="MultiDevice_PostOOBESetupFlow_Page" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -132,7 +132,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.GattConnectionToAuthentication.EffectiveSuccessRateWithRetries"
-    enum="BooleanSuccess" expires_after="2021-04-04">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -161,7 +161,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.ReceiveAdvertisementToConnectionDuration.Background"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -194,7 +194,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToConnectionDuration.Background"
-    units="ms" expires_after="2021-04-04">
+    units="ms" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -238,7 +238,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.ReceiveAdvertisementToGattConnection.EffectiveSuccessRateWithRetries"
-    enum="BooleanSuccess" expires_after="2021-04-04">
+    enum="BooleanSuccess" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -386,7 +386,7 @@
 </histogram>
 
 <histogram name="MultiDevice.Setup.HostVerifier.DoesHostHaveCryptoData"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>nohle@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -401,7 +401,7 @@
 
 <histogram name="MultiDevice.VerifyButtonClicked"
     enum="MultiDevice_VerifyAndForgetHostConfirmationState"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/navigation/histograms.xml b/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
index 7404937e..14f8ebe 100644
--- a/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
@@ -422,7 +422,7 @@
 
 <histogram
     name="BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName"
-    enum="MojoInterfaceName" expires_after="2021-05-09">
+    enum="MojoInterfaceName" expires_after="2021-08-09">
   <owner>carlscab@google.com</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -717,7 +717,7 @@
 </histogram>
 
 <histogram name="Navigation.IsSameBrowsingInstance"
-    enum="NavigationIsSameBrowsingInstance" expires_after="2021-05-09">
+    enum="NavigationIsSameBrowsingInstance" expires_after="2021-08-09">
   <owner>arthursonzogni@chromium.org</owner>
   <owner>clamy@chromium.org</owner>
   <owner>nasko@chromium.org</owner>
@@ -783,7 +783,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrame.SiteEngagementLevel"
-    enum="SiteEngagementLevel" expires_after="2021-06-06">
+    enum="SiteEngagementLevel" expires_after="2021-08-09">
   <owner>meacer@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -813,7 +813,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrameHasRTLDomainDifferentPage2" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -858,7 +858,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrameSchemeDifferentPageOTR2"
-    enum="NavigationScheme" expires_after="2021-06-01">
+    enum="NavigationScheme" expires_after="2021-08-09">
   <owner>elawrence@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/net/histograms.xml b/tools/metrics/histograms/histograms_xml/net/histograms.xml
index 603f931..a425fe7 100644
--- a/tools/metrics/histograms/histograms_xml/net/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/net/histograms.xml
@@ -51,7 +51,7 @@
 </histogram>
 
 <histogram name="Net.AlternateServiceFailed" enum="NetErrorCodes"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
@@ -299,7 +299,7 @@
 </histogram>
 
 <histogram base="true" name="Net.CertVerifier.NameNormalizationPrivateRoots"
-    enum="NetCertificateNameNormalization" expires_after="2021-04-26">
+    enum="NetCertificateNameNormalization" expires_after="2021-08-09">
   <owner>mattm@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -374,7 +374,7 @@
 </histogram>
 
 <histogram name="Net.ConnectionInfo.SubResource" enum="ConnectionInfo"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -445,7 +445,7 @@
 </histogram>
 
 <histogram name="Net.CountOfBrokenAlternativeServices" units="services"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -469,7 +469,7 @@
 </histogram>
 
 <histogram name="Net.CountOfRecentlyBrokenAlternativeServices" units="services"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -590,7 +590,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsTask.SuccessTime" units="ms"
-    expires_after="2021-06-04">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -1205,7 +1205,7 @@
 </histogram>
 
 <histogram name="Net.DNS.SecureDnsMode.Off.ResolveTime" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>doh-core@google.com</owner>
   <summary>
@@ -1240,7 +1240,7 @@
 </histogram>
 
 <histogram name="Net.DNS.SecureDnsTask.DnsModeSecure.FailureTime" units="ms"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>doh-core@google.com</owner>
   <summary>
@@ -1290,7 +1290,7 @@
 </histogram>
 
 <histogram name="Net.DNS.UI.ProbeAttemptSuccess" enum="Boolean"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>doh-core@google.com</owner>
   <summary>
@@ -1300,7 +1300,7 @@
 </histogram>
 
 <histogram name="Net.DNS.UI.ValidationAttemptSuccess" enum="Boolean"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>doh-core@google.com</owner>
   <summary>
@@ -1585,7 +1585,7 @@
 </histogram>
 
 <histogram name="Net.HttpAuthCount" enum="HttpAuthCount"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>asanka@chromium.org</owner>
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -1614,7 +1614,7 @@
 </histogram>
 
 <histogram name="Net.HttpAuthTarget" enum="HttpAuthTarget"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>asanka@chromium.org</owner>
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -2418,7 +2418,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.AbortedPendingStreamRequests"
-    units="stream requests" expires_after="2021-05-11">
+    units="stream requests" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2469,7 +2469,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.CertVerificationResult" enum="NetErrorCodes"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2540,7 +2540,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ClosedDuringInitializeSession" enum="Boolean"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2715,7 +2715,7 @@
 
 <histogram
     name="Net.QuicSession.ConnectionCloseErrorCodeServerIetfApplicationGQuicErrorMissing"
-    enum="QuicHttp3ErrorCodes" expires_after="2021-05-11">
+    enum="QuicHttp3ErrorCodes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2758,7 +2758,7 @@
 
 <histogram
     name="Net.QuicSession.ConnectionCloseErrorCodeServerIetfTransportGQuicErrorMissing"
-    enum="QuicTransportErrorCodes" expires_after="2021-05-11">
+    enum="QuicTransportErrorCodes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2796,7 +2796,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ConnectionFlowControlBlocked"
-    enum="BooleanBlocked" expires_after="2021-05-11">
+    enum="BooleanBlocked" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -2929,7 +2929,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.EncryptionEstablishedTime"
-    units="Milliseconds" expires_after="2021-05-11">
+    units="Milliseconds" expires_after="2021-08-09">
   <owner>fayang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3042,7 +3042,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.HeaderCompressionRatioHpackReceived" units="%"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3051,7 +3051,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.HeaderCompressionRatioHpackSent" units="%"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3060,7 +3060,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.HeaderCompressionRatioQpackReceived" units="%"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3182,7 +3182,7 @@
 
 <histogram
     name="Net.QuicSession.LastInFlightPacketSentTimeFromHandshakeCompletionWithPublicReset"
-    units="ms" expires_after="2021-05-11">
+    units="ms" expires_after="2021-08-09">
   <owner>fayang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3192,7 +3192,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.LastSentPacketContentBeforePublicReset"
-    units="bitfield value" expires_after="2021-05-11">
+    units="bitfield value" expires_after="2021-08-09">
   <owner>fayang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3222,7 +3222,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.MaxReordering" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3325,7 +3325,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.NumPingsSent" units="pings"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>renjietang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3344,7 +3344,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.NumTotalStreams" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3458,7 +3458,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.PendingStreamsWaitTime" units="ms"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3632,7 +3632,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ReceivedSettings.BlockedStreams" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3642,7 +3642,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ReceivedSettings.CountPlusOne" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3652,7 +3652,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ReceivedSettings.MaxHeaderListSize2"
-    units="bytes" expires_after="2021-05-11">
+    units="bytes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3662,7 +3662,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ReceivedSettings.MaxTableCapacity2"
-    units="bytes" expires_after="2021-05-11">
+    units="bytes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3672,7 +3672,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ReceivedSettings.ReservedCountPlusOne"
-    units="units" expires_after="2021-05-11">
+    units="units" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3726,7 +3726,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.RstStreamErrorCodeServer"
-    enum="QuicRstStreamErrorCodes" expires_after="2021-05-11">
+    enum="QuicRstStreamErrorCodes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3757,7 +3757,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.SendPacketSize" units="bytes"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>zhongyi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3832,7 +3832,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.StreamCloseErrorCodeClient.HandshakeConfirmed"
-    enum="QuicErrorCodes" expires_after="2021-05-11">
+    enum="QuicErrorCodes" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3852,7 +3852,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.StreamFlowControlBlocked"
-    enum="BooleanBlocked" expires_after="2021-05-11">
+    enum="BooleanBlocked" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3974,7 +3974,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.UndecryptablePacketsReceived" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -4302,7 +4302,7 @@
 </histogram>
 
 <histogram name="Net.QuicTransportClient.ConnectionError" enum="NetErrorCodes"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>vasilvv@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -4312,7 +4312,7 @@
 </histogram>
 
 <histogram name="Net.QuicTransportClient.Error" enum="NetErrorCodes"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>vasilvv@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>The code for QuicTransport errors.</summary>
@@ -4452,7 +4452,7 @@
 </histogram>
 
 <histogram name="Net.SpdyFrameStreamAndSessionFlowControlState"
-    enum="SpdyFrameFlowControlState" expires_after="2021-05-11">
+    enum="SpdyFrameFlowControlState" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -4527,7 +4527,7 @@
 </histogram>
 
 <histogram name="Net.SpdySession.PushedAndUnclaimedBytes" units="count"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>zhongyi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -4536,7 +4536,7 @@
 </histogram>
 
 <histogram name="Net.SpdySession.PushedBytes" units="count"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>zhongyi@chromium.org</owner>
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
@@ -4592,7 +4592,7 @@
 </histogram>
 
 <histogram name="Net.SpdyStreamsAbandonedPerSession" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -4601,7 +4601,7 @@
 </histogram>
 
 <histogram name="Net.SpdyStreamsPerSession" units="units"
-    expires_after="2021-05-11">
+    expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>The number of streams issued over a single session.</summary>
@@ -4707,7 +4707,7 @@
 </histogram>
 
 <histogram name="Net.SSLHandshakeEarlyDataReason"
-    enum="SSLHandshakeEarlyDataReason" expires_after="2021-06-01">
+    enum="SSLHandshakeEarlyDataReason" expires_after="2021-08-09">
   <owner>davidben@chromium.org</owner>
   <owner>svaldez@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
@@ -4755,7 +4755,7 @@
 </histogram>
 
 <histogram name="Net.SSLRSAKeyUsage.UnknownRoot" enum="RSAKeyUsage"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>davidben@chromium.org</owner>
   <summary>
     For each TLS connection which uses a unknown root, an RSA key, and TLS 1.2
@@ -5036,7 +5036,7 @@
   <summary>Chromium error code from call to RandomBind() UDP socket.</summary>
 </histogram>
 
-<histogram name="Net.UDPSocketWinClose" units="ms" expires_after="2021-05-11">
+<histogram name="Net.UDPSocketWinClose" units="ms" expires_after="2021-08-09">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>The time spent in closesocket call in UDPSocketWin::Close.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml
index e588f87..d154e2f 100644
--- a/tools/metrics/histograms/histograms_xml/network/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -2112,7 +2112,7 @@
 </histogram>
 
 <histogram name="NetworkService.CorsForcedOffForIsolatedWorldOrigin"
-    enum="BooleanForceDisabled" expires_after="M92">
+    enum="BooleanForceDisabled" expires_after="2021-08-09">
   <owner>lukasza@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
index bb13104..0050e9e4 100644
--- a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
@@ -34,7 +34,7 @@
 </histogram>
 
 <histogram name="NewTabPage.ActioniOS" enum="NewTabPageActioniOS"
-    expires_after="2021-06-10">
+    expires_after="2021-08-09">
   <owner>justincohen@chromium.org</owner>
   <owner>gambard@chromium.org</owner>
   <summary>
@@ -917,7 +917,7 @@
 </histogram>
 
 <histogram name="NewTabPage.MostVisitedAge" units="seconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1227,7 +1227,7 @@
 </histogram>
 
 <histogram name="NewTabPage.SearchAvailableLoadTime2.ColdStart" units="ms"
-    expires_after="2021-06-08">
+    expires_after="2021-08-09">
   <owner>fgorski@chromium.org</owner>
   <owner>ender@google.com</owner>
   <owner>yyushkina@chromium.org</owner>
@@ -1239,7 +1239,7 @@
 </histogram>
 
 <histogram name="NewTabPage.SearchAvailableLoadTime2.WarmStart" units="ms"
-    expires_after="2021-06-08">
+    expires_after="2021-08-09">
   <owner>fgorski@chromium.org</owner>
   <owner>ender@google.com</owner>
   <owner>yyushkina@chromium.org</owner>
@@ -1506,7 +1506,7 @@
 </histogram>
 
 <histogram name="NewTabPage.SuggestionsImpressionAge" units="seconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml b/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
index 917d01e..3852848 100644
--- a/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
@@ -581,7 +581,7 @@
 </histogram>
 
 <histogram name="Omnibox.OnDeviceHeadSuggest.ResultCount" units="count"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>cch@chromium.org</owner>
   <owner>suggest-2g@google.com</owner>
   <summary>
@@ -729,7 +729,7 @@
 </histogram>
 
 <histogram name="Omnibox.SelectedPosition" units="position"
-    expires_after="2021-08-01">
+    expires_after="2021-08-09">
   <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/oobe/histograms.xml b/tools/metrics/histograms/histograms_xml/oobe/histograms.xml
index fc857fdb..17d0ca29 100644
--- a/tools/metrics/histograms/histograms_xml/oobe/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/oobe/histograms.xml
@@ -102,7 +102,7 @@
 </histogram>
 
 <histogram name="OOBE.FingerprintSetupScreen.UserActions"
-    enum="FingerprintSetupScreenUserAction" expires_after="2021-06-06">
+    enum="FingerprintSetupScreenUserAction" expires_after="2021-08-09">
   <owner>raleksandrov@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -122,7 +122,7 @@
 </histogram>
 
 <histogram base="true" name="OOBE.GestureNavigationScreen.PageShownTime"
-    units="ms" expires_after="2021-04-01">
+    units="ms" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes
      name="GestureNavigationOOBEPage" -->
 
@@ -167,7 +167,7 @@
 </histogram>
 
 <histogram name="OOBE.MarketingOptInScreen.GeolocationResolveLength"
-    units="chars" expires_after="2021-05-30">
+    units="chars" expires_after="2021-08-09">
   <owner>rrsilva@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -289,7 +289,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.Behavior" enum="SyncConsentBehavior"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jamescook@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -301,7 +301,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.ReviewFollowingSetup"
-    enum="BooleanChecked" expires_after="2021-04-11">
+    enum="BooleanChecked" expires_after="2021-08-09">
   <owner>raleksandrov@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -311,7 +311,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.SyncEnabled" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jamescook@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -322,7 +322,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.UserChoice"
-    enum="SyncConsentUserChoice" expires_after="2021-06-06">
+    enum="SyncConsentUserChoice" expires_after="2021-08-09">
   <owner>jamescook@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/optimization/histograms.xml b/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
index 0f280a6..eb0f2ba 100644
--- a/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
@@ -22,7 +22,7 @@
 <histograms>
 
 <histogram base="true" name="OptimizationGuide.ApplyDecision"
-    enum="OptimizationGuideOptimizationTypeDecision" expires_after="2021-06-06">
+    enum="OptimizationGuideOptimizationTypeDecision" expires_after="2021-08-09">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -32,7 +32,7 @@
 </histogram>
 
 <histogram base="true" name="OptimizationGuide.ApplyDecisionAsync"
-    enum="OptimizationGuideOptimizationTypeDecision" expires_after="2021-06-06">
+    enum="OptimizationGuideOptimizationTypeDecision" expires_after="2021-08-09">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -106,7 +106,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -127,7 +127,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount"
-    units="total host count" expires_after="2021-05-16">
+    units="total host count" expires_after="2021-08-09">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -137,7 +137,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode"
-    enum="NetErrorCodes" expires_after="2021-06-06">
+    enum="NetErrorCodes" expires_after="2021-08-09">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -147,7 +147,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.Status"
-    enum="HttpResponseCode" expires_after="2021-06-06">
+    enum="HttpResponseCode" expires_after="2021-08-09">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -227,7 +227,7 @@
 <histogram
     name="OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus"
     enum="OptimizationGuideRaceNavigationFetchAttemptStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -463,7 +463,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.RemoteFetchingEnabled"
-    units="BooleanEnabled" expires_after="2021-06-06">
+    units="BooleanEnabled" expires_after="2021-08-09">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 78e4c138..d778aa0 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -144,7 +144,7 @@
 </histogram>
 
 <histogram name="AccountManager.Migrations.Result" enum="BooleanSuccess"
-    expires_after="2021-03-28">
+    expires_after="2021-08-09">
   <owner>sinhak@chromium.org</owner>
   <summary>
     Tracks the final result of migrating accounts to Chrome OS Account Manager.
@@ -166,7 +166,7 @@
 </histogram>
 
 <histogram name="AccountManager.MirrorReauthenticationRequest"
-    enum="BooleanHit" expires_after="2021-03-28">
+    enum="BooleanHit" expires_after="2021-08-09">
   <owner>sinhak@chromium.org</owner>
   <owner>anastasiian@chromium.org</owner>
   <summary>
@@ -186,7 +186,7 @@
 </histogram>
 
 <histogram name="AccountManager.TokenLoadStatus"
-    enum="AccountManagerTokenLoadStatus" expires_after="2021-06-07">
+    enum="AccountManagerTokenLoadStatus" expires_after="2021-08-09">
   <owner>sinhak@chromium.org</owner>
   <owner>anastasiian@chromium.org</owner>
   <summary>
@@ -352,7 +352,7 @@
 </histogram>
 
 <histogram name="Ads.Media.LoadType" enum="MediaLoadType"
-    expires_after="2021-04-11">
+    expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -398,7 +398,7 @@
 </histogram>
 
 <histogram name="AnchorElementMetrics.Clicked.OnDSE.SameHost"
-    enum="BooleanAnchorElementSameHost" expires_after="2021-05-02">
+    enum="BooleanAnchorElementSameHost" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <summary>
     True if the target link of the anchor element has the same host as the root
@@ -581,7 +581,7 @@
 </histogram>
 
 <histogram name="AppBanners.InstallableStatusCode"
-    enum="AppBannersInstallableStatusCode" expires_after="2021-06-06">
+    enum="AppBannersInstallableStatusCode" expires_after="2021-08-09">
   <owner>dominickn@chromium.org</owner>
   <owner>pjmclachlan@google.com</owner>
   <summary>
@@ -668,14 +668,14 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.Canceled" enum="BooleanCanceled"
-    expires_after="2021-05-25">
+    expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>Tracks whether the update job was canceled.</summary>
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingCorruptionFixedInUpdate"
-    units="resources" expires_after="2021-05-25">
+    units="resources" expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -688,7 +688,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceCheck" units="units"
-    expires_after="2021-05-25">
+    expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -698,7 +698,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceCorrupt" units="units"
-    expires_after="2021-05-25">
+    expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -729,7 +729,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceNotCorrupt"
-    units="resources" expires_after="2021-05-25">
+    units="resources" expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -742,7 +742,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceOnlyCorrupt"
-    units="resources" expires_after="2021-05-25">
+    units="resources" expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>enne@chromium.org</owner>
   <summary>
@@ -757,7 +757,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceOnlyNotCorrupt"
-    units="resources" expires_after="2021-05-25">
+    units="resources" expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>enne@chromium.org</owner>
   <summary>
@@ -772,7 +772,7 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.ExistingResourceReused" units="units"
-    expires_after="2021-05-25">
+    expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -782,14 +782,14 @@
 </histogram>
 
 <histogram name="appcache.UpdateJob.FinalInternalState"
-    enum="AppCacheUpdateJobInternalState" expires_after="2021-05-25">
+    enum="AppCacheUpdateJobInternalState" expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>Tracks the final internal state for the update job.</summary>
 </histogram>
 
 <histogram name="appcache.UpdateJob.ResourceFreshness" units="days"
-    expires_after="2021-05-25">
+    expires_after="2021-08-09">
   <owner>cmp@chromium.org</owner>
   <owner>pwnall@chromium.org</owner>
   <summary>
@@ -821,7 +821,7 @@
 </histogram>
 
 <histogram name="AppManagement.EntryPoints" enum="AppManagementEntryPoint"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>jshikaram@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>The ways the user opens up the App Management interface.</summary>
@@ -1572,7 +1572,7 @@
 </histogram>
 
 <histogram name="Bookmarks.BookmarkAllTabsWithTabsCount.Incognito" units="tabs"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
@@ -1623,7 +1623,7 @@
 </histogram>
 
 <histogram name="Bookmarks.Count.OnProfileLoad" units="bookmarks"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>supertri@chromium.org</owner>
   <owner>isherman@chromium.org</owner>
   <owner>aidanday@google.com</owner>
@@ -1800,7 +1800,7 @@
 </histogram>
 
 <histogram name="Bookmarks.ReadingList.NumberOfUnreadItems" units="items"
-    expires_after="M91">
+    expires_after="2021-08-09">
   <owner>shaktisahu@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
   <summary>
@@ -1810,7 +1810,7 @@
 </histogram>
 
 <histogram name="Bookmarks.StarEntryPoint.ClickedAction"
-    enum="StarEntryPointAction" expires_after="M92">
+    enum="StarEntryPointAction" expires_after="2021-08-09">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -1930,7 +1930,7 @@
 </histogram>
 
 <histogram name="CaptivePortal.ProbeReason" enum="CaptivePortalProbeReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>doh-core@google.com</owner>
   <owner>cros-networking@google.com</owner>
@@ -2287,7 +2287,7 @@
 </histogram>
 
 <histogram name="ChildProcess.Launched.UtilityProcessHash"
-    enum="UtilityProcessNameHash" expires_after="2021-06-06">
+    enum="UtilityProcessNameHash" expires_after="2021-08-09">
   <owner>wfh@chromium.org</owner>
   <summary>
     Count of child utility process launches, bucketed by the hash of their
@@ -2571,7 +2571,7 @@
 </histogram>
 
 <histogram name="Clipboard.ExtensionContentScriptReadHasUserActivation"
-    units="proportion" expires_after="2021-06-06">
+    units="proportion" expires_after="2021-08-09">
   <owner>huangdarwin@chromium.org</owner>
   <owner>src/third_party/blink/renderer/modules/clipboard/OWNERS</owner>
   <summary>
@@ -2690,7 +2690,7 @@
 </histogram>
 
 <histogram base="true" name="CompositorLatency.CompositorOnlyFrame"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
@@ -2721,7 +2721,7 @@
 </histogram>
 
 <histogram base="true" name="CompositorLatency.DroppedFrame"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
@@ -3194,7 +3194,7 @@
 </histogram>
 
 <histogram name="ContextMenu.ViewsTextServices.Emoji" enum="Boolean"
-    expires_after="M91">
+    expires_after="2021-08-09">
   <owner>ramyan@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <summary>
@@ -3204,7 +3204,7 @@
 </histogram>
 
 <histogram name="ContextMenu.WaitingForElementDetails" enum="BooleanHit"
-    expires_after="2021-05-23">
+    expires_after="2021-08-09">
   <owner>michaeldo@chromium.org</owner>
   <owner>src/ios/web/OWNERS</owner>
   <summary>
@@ -3225,7 +3225,7 @@
 </histogram>
 
 <histogram name="Conversions.ExtraReportDelay" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -3250,7 +3250,7 @@
 </histogram>
 
 <histogram name="Conversions.ReportStatus" enum="ConversionReportStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -3797,7 +3797,8 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.ActiveApp" enum="DemoModeApp" expires_after="M92">
+<histogram name="DemoMode.ActiveApp" enum="DemoModeApp"
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3806,7 +3807,8 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.AppLaunched" enum="DemoModeApp" expires_after="M92">
+<histogram name="DemoMode.AppLaunched" enum="DemoModeApp"
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3817,7 +3819,7 @@
 </histogram>
 
 <histogram name="DemoMode.AppLaunchSource" enum="DemoModeAppLaunchSource"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3826,7 +3828,7 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.DwellTime" units="seconds" expires_after="M92">
+<histogram name="DemoMode.DwellTime" units="seconds" expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3838,7 +3840,7 @@
 </histogram>
 
 <histogram name="DemoMode.IdleLogoutWarningEvent"
-    enum="DemoModeIdleLogoutWarningEvent" expires_after="M92">
+    enum="DemoModeIdleLogoutWarningEvent" expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3876,7 +3878,8 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.SessionLength" units="minutes" expires_after="M92">
+<histogram name="DemoMode.SessionLength" units="minutes"
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3890,7 +3893,7 @@
 </histogram>
 
 <histogram name="DemoMode.Setup.DownloadDuration" units="minutes"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3900,7 +3903,7 @@
 </histogram>
 
 <histogram name="DemoMode.Setup.EnrollDuration" units="minutes"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3910,7 +3913,7 @@
 </histogram>
 
 <histogram name="DemoMode.Setup.LoadingDuration" units="minutes"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3921,7 +3924,8 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.Setup.NumRetries" units="units" expires_after="M92">
+<histogram name="DemoMode.Setup.NumRetries" units="units"
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -3931,7 +3935,8 @@
   </summary>
 </histogram>
 
-<histogram name="DemoMode.UniqueAppsLaunched" units="units" expires_after="M92">
+<histogram name="DemoMode.UniqueAppsLaunched" units="units"
+    expires_after="2021-08-09">
   <owner>drcrash@chromium.org</owner>
   <owner>cros-demo-mode-eng@google.com</owner>
   <summary>
@@ -4087,7 +4092,7 @@
 </histogram>
 
 <histogram name="Discarding.OnCriticalPressure.TotalRSS_PercentOfRAM" units="%"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>sebmarchand@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -4269,7 +4274,7 @@
   </summary>
 </histogram>
 
-<histogram name="DnsProbe.ProbeDuration2" units="ms" expires_after="2021-06-04">
+<histogram name="DnsProbe.ProbeDuration2" units="ms" expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -4278,7 +4283,7 @@
 </histogram>
 
 <histogram name="DnsProbe.ProbeResult" enum="DnsProbe.ProbeStatus"
-    expires_after="2021-06-04">
+    expires_after="2021-08-09">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>Result of DNS diagnostics probes sent by the probe service.</summary>
@@ -4577,7 +4582,7 @@
 </histogram>
 
 <histogram name="Downgrade.TakeSnapshot.FailureCount" units="count"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <owner>ydago@chromium.org</owner>
   <summary>
@@ -4587,7 +4592,7 @@
 </histogram>
 
 <histogram name="Downgrade.TakeSnapshot.ItemFailure" enum="SnapshotItemId"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <owner>ydago@chromium.org</owner>
   <summary>
@@ -4646,7 +4651,7 @@
 </histogram>
 
 <histogram name="Downgrade.Type" enum="UserDataDowngradeType"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <summary>
     The type of User Data downgrade detected, if any. The &quot;none&quot;
@@ -4695,7 +4700,7 @@
 </histogram>
 
 <histogram name="DriveCommon.Lifecycle.FirstLaunchTime" units="ms"
-    expires_after="2021-04-19">
+    expires_after="2021-08-09">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -4971,7 +4976,8 @@
   </summary>
 </histogram>
 
-<histogram name="EphemeralTab.Ctr" enum="BooleanOpened" expires_after="M92">
+<histogram name="EphemeralTab.Ctr" enum="BooleanOpened"
+    expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -4980,7 +4986,8 @@
   </summary>
 </histogram>
 
-<histogram name="EphemeralTab.CtrPeek" enum="BooleanOpened" expires_after="M92">
+<histogram name="EphemeralTab.CtrPeek" enum="BooleanOpened"
+    expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -4989,7 +4996,8 @@
   </summary>
 </histogram>
 
-<histogram name="EphemeralTab.DurationOpened" units="ms" expires_after="M92">
+<histogram name="EphemeralTab.DurationOpened" units="ms"
+    expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -4998,7 +5006,8 @@
   </summary>
 </histogram>
 
-<histogram name="EphemeralTab.DurationPeeked" units="ms" expires_after="M92">
+<histogram name="EphemeralTab.DurationPeeked" units="ms"
+    expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -5176,7 +5185,7 @@
 </histogram>
 
 <histogram name="ExploreSites.RequestStatus" enum="ExploreSitesRequestStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>dimich@chromium.org</owner>
   <owner>freedjm@chromium.org</owner>
   <summary>
@@ -5186,7 +5195,7 @@
 </histogram>
 
 <histogram name="ExploreSites.SiteTilesClickIndex2" units="units"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>dewittj@chromium.org</owner>
   <owner>petewil@chromium.org</owner>
   <summary>
@@ -5844,7 +5853,7 @@
 </histogram>
 
 <histogram name="Gamepad.KnownGamepadConnectedWithId"
-    enum="GamepadVendorProduct" expires_after="2021-06-06">
+    enum="GamepadVendorProduct" expires_after="2021-08-09">
   <owner>mattreynolds@chromium.org</owner>
   <owner>deviceapi-team@google.com</owner>
   <summary>
@@ -6526,7 +6535,7 @@
 </histogram>
 
 <histogram base="true" name="GridTabSwitcher.FramePerSecond" units="frame/sec"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -6600,7 +6609,7 @@
   </summary>
 </histogram>
 
-<histogram name="Hardware.TotalDiskSpace" units="GB" expires_after="2021-05-02">
+<histogram name="Hardware.TotalDiskSpace" units="GB" expires_after="2021-08-09">
   <owner>sadrul@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -6610,7 +6619,7 @@
 </histogram>
 
 <histogram name="HeapProfiling.ProfiledProcess.Type"
-    enum="HeapProfilingProcessType" expires_after="2021-05-02">
+    enum="HeapProfilingProcessType" expires_after="2021-08-09">
   <owner>erikchen@chromium.org</owner>
   <owner>chrome-memory@google.com</owner>
   <summary>
@@ -6794,7 +6803,7 @@
   </summary>
 </histogram>
 
-<histogram name="HttpCache.AccessToDone" units="ms" expires_after="2021-05-02">
+<histogram name="HttpCache.AccessToDone" units="ms" expires_after="2021-08-09">
   <owner>morlovich@chromium.org</owner>
   <summary>
     For every http cache transaction with a pattern (see HttpCache.Pattern), the
@@ -6859,7 +6868,7 @@
 </histogram>
 
 <histogram name="HttpCache.MaxFileSizeOnInit" units="KB"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>shivanisha@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -7033,7 +7042,7 @@
 </histogram>
 
 <histogram name="ImportantFile.FileDeleteRetrySuccessCount"
-    units="attept number" expires_after="2021-05-02">
+    units="attept number" expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <owner>xaerox@yandex-team.ru</owner>
   <summary>
@@ -7073,7 +7082,7 @@
 </histogram>
 
 <histogram name="ImportantFile.SerializationDuration" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>battre@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>CPU time used to serialize preferences into a JSON string.</summary>
@@ -8020,7 +8029,7 @@
 </histogram>
 
 <histogram name="Launch.HomeScreenSource" enum="LaunchFromHomeScreenSource"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>dominickn@chromium.org</owner>
   <owner>hartmanng@chromium.org</owner>
   <owner>peconn@chromium.org</owner>
@@ -8594,7 +8603,7 @@
 </histogram>
 
 <histogram base="true" name="LoadingPredictor.PreconnectLearningRecall"
-    units="%" expires_after="2021-04-18">
+    units="%" expires_after="2021-08-09">
   <owner>alexilin@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -8921,7 +8930,7 @@
 </histogram>
 
 <histogram name="Manifest.HasProperty" enum="Boolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mgiuca@chromium.org</owner>
   <owner>mlamouri@chromium.org</owner>
   <summary>
@@ -9310,7 +9319,7 @@
 </histogram>
 
 <histogram base="true" name="Mouse.ScrollAcceleration" enum="BooleanEnabled"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="PreferenceChangeType" -->
 
   <owner>khorimoto@chromium.org</owner>
@@ -9379,7 +9388,7 @@
 </histogram>
 
 <histogram name="MultiDeviceSetup.OOBE.UserChoice"
-    enum="MultiDeviceSetupOOBEUserChoice" expires_after="2021-06-06">
+    enum="MultiDeviceSetupOOBEUserChoice" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <owner>hsuregan@chromium.org</owner>
@@ -9390,7 +9399,7 @@
 </histogram>
 
 <histogram name="MultiDeviceSetup_NotificationClicked"
-    enum="MultiDeviceSetupNotification" expires_after="2021-06-06">
+    enum="MultiDeviceSetupNotification" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -9400,7 +9409,7 @@
 </histogram>
 
 <histogram name="MultiDeviceSetup_NotificationDismissed"
-    enum="MultiDeviceSetupNotification" expires_after="2021-04-04">
+    enum="MultiDeviceSetupNotification" expires_after="2021-08-09">
   <owner>vecore@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -9581,7 +9590,7 @@
 </histogram>
 
 <histogram name="NativeTheme.GetSystemColor.UsesColorProvider" enum="Boolean"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>pkasting@chromium.org</owner>
   <owner>robliao@chromium.org</owner>
   <summary>Records if the color provider computed the color.</summary>
@@ -10473,7 +10482,7 @@
 </histogram>
 
 <histogram name="NQE.CachedNetworkQualityAvailable" enum="BooleanAvailable"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -10483,7 +10492,7 @@
 </histogram>
 
 <histogram name="NQE.CellularSignalStrength.ECTReduction"
-    units="ECT level reduction" expires_after="2021-06-06">
+    units="ECT level reduction" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <summary>
     Number of buckets by which effective connection type was reduced or capped
@@ -10493,7 +10502,7 @@
 </histogram>
 
 <histogram name="NQE.CellularSignalStrength.LevelAvailable"
-    enum="BooleanAvailable" expires_after="2021-06-06">
+    enum="BooleanAvailable" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -10541,7 +10550,7 @@
 </histogram>
 
 <histogram name="NQE.EndToEndRTT.OnECTComputation" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>src/net/nqe/OWNERS</owner>
   <summary>
@@ -10582,7 +10591,7 @@
   </summary>
 </histogram>
 
-<histogram name="NQE.Prefs.ReadCount" units="count" expires_after="2021-06-06">
+<histogram name="NQE.Prefs.ReadCount" units="count" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -10591,7 +10600,7 @@
   </summary>
 </histogram>
 
-<histogram name="NQE.Prefs.ReadSize" units="count" expires_after="2021-06-06">
+<histogram name="NQE.Prefs.ReadSize" units="count" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -10600,7 +10609,7 @@
   </summary>
 </histogram>
 
-<histogram name="NQE.Prefs.WriteCount" units="count" expires_after="2021-06-06">
+<histogram name="NQE.Prefs.WriteCount" units="count" expires_after="2021-08-09">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -10763,7 +10772,7 @@
 </histogram>
 
 <histogram name="OriginTrials.ValidationResult" enum="OriginTrialTokenStatus"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>chasej@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>feature-control@chromium.org</owner>
@@ -11157,7 +11166,7 @@
 </histogram>
 
 <histogram name="PartnerBookmark.Count2" units="bookmarks"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bttk@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -11188,7 +11197,7 @@
 </histogram>
 
 <histogram name="PDF.Actions" enum="ChromePDFViewerActions"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>hnakashima@chromium.org</owner>
   <summary>
     Tracks user actions in the PDF viewer. Logged when the document is opened
@@ -11260,7 +11269,7 @@
   </summary>
 </histogram>
 
-<histogram name="PDF.Version" enum="PDFVersion" expires_after="2021-06-06">
+<histogram name="PDF.Version" enum="PDFVersion" expires_after="2021-08-09">
   <owner>dhoss@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>Tracks versions of documents opened in the PDF viewer.</summary>
@@ -11708,7 +11717,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.BodySize" units="bytes"
-    expires_after="2021-05-30">
+    expires_after="2021-08-09">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -11731,7 +11740,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.Count" units="count"
-    expires_after="2021-05-30">
+    expires_after="2021-08-09">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -11925,7 +11934,7 @@
 </histogram>
 
 <histogram name="Previews.CacheControlNoTransform.BlockedPreview"
-    enum="PreviewsType" expires_after="2021-06-06">
+    enum="PreviewsType" expires_after="2021-08-09">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <owner>src/components/previews/OWNERS</owner>
@@ -12028,7 +12037,7 @@
 </histogram>
 
 <histogram name="Previews.LitePageNotificationInfoBar"
-    enum="PreviewsLitePageInfoBarAction" expires_after="2021-06-06">
+    enum="PreviewsLitePageInfoBarAction" expires_after="2021-08-09">
   <owner>robertogden@chromium.org</owner>
   <owner>src/components/data_reduction_proxy/OWNERS</owner>
   <summary>
@@ -12037,7 +12046,7 @@
 </histogram>
 
 <histogram name="Previews.OmniboxAction" enum="PreviewsUserOmniboxAction"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>robertogden@chromium.org</owner>
   <summary>User interactions with the Previews Android Omnibox UI.</summary>
 </histogram>
@@ -12062,7 +12071,7 @@
 </histogram>
 
 <histogram name="Previews.PageEndReason" enum="PageEndReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>robertogden@chromium.org</owner>
   <summary>Records why the page load ended on a given preview type.</summary>
 </histogram>
@@ -12955,7 +12964,7 @@
 </histogram>
 
 <histogram name="RenderTextHarfBuzz.GetFallbackFontsTime" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>ccameron@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -12966,7 +12975,7 @@
 </histogram>
 
 <histogram name="RenderTextHarfBuzz.GetFallbackFontTime" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>ccameron@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -12977,7 +12986,7 @@
 </histogram>
 
 <histogram name="RenderTextHarfBuzz.ShapeRunsFallback" enum="ShapeRunFallback"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>ccameron@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -12987,7 +12996,7 @@
 </histogram>
 
 <histogram name="RenderTextHarfBuzz.ShapeRunsWithFallbackFontsTime" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>ccameron@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -14261,7 +14270,7 @@
 </histogram>
 
 <histogram name="SignedExchange.CertVerificationResult" enum="NetErrorCodes"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -14329,7 +14338,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Prefetch.LoadResult2"
-    enum="SignedExchangeLoadResult" expires_after="2021-06-06">
+    enum="SignedExchangeLoadResult" expires_after="2021-08-09">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -14945,7 +14954,7 @@
 </histogram>
 
 <histogram name="Spellcheck.Android.Available" enum="BooleanAvailable"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>timvolodine@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -14989,7 +14998,7 @@
 </histogram>
 
 <histogram name="SpellCheck.SpellingService.Enabled" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>groby@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -15352,7 +15361,7 @@
 </histogram>
 
 <histogram name="Style.InvalidationTime" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in StyleEngine::InvalidateStyle. Only samples from high
@@ -15361,7 +15370,7 @@
 </histogram>
 
 <histogram name="Style.RebuildLayoutTreeTime" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in RebuildLayoutTree called from Document::UpdateStyle.
@@ -15369,13 +15378,51 @@
 </histogram>
 
 <histogram name="Style.RecalcTime" units="microseconds"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in RecalcStyle called from Document::UpdateStyle.
   </summary>
 </histogram>
 
+<histogram name="SubresourceWebBundles.ContentLength" units="bytes"
+    expires_after="M94">
+  <owner>horo@chromium.org</owner>
+  <owner>webpackage-dev@chromium.org</owner>
+  <summary>
+    The value of content length header of a subresource web bundle, or zero if
+    the content-length header is missing.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceWebBundles.LoadResult"
+    enum="SubresourceWebBundleLoadResult" expires_after="M94">
+  <owner>horo@chromium.org</owner>
+  <owner>webpackage-dev@chromium.org</owner>
+  <summary>The result of loading subresource web bundles.</summary>
+</histogram>
+
+<histogram name="SubresourceWebBundles.MaxMemoryUsagePerProcess" units="bytes"
+    expires_after="M94">
+  <owner>horo@chromium.org</owner>
+  <owner>webpackage-dev@chromium.org</owner>
+  <summary>
+    The max memory usage per renderer process for subresource web bundles which
+    are kept in the network process's memory. Recorded when all the subresource
+    web bundles for the renderer process are released.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceWebBundles.ReceivedSize" units="bytes"
+    expires_after="M94">
+  <owner>horo@chromium.org</owner>
+  <owner>webpackage-dev@chromium.org</owner>
+  <summary>
+    The received data size of a subresource web bundle. Recorded when received
+    all the body of the subresource web bundle.
+  </summary>
+</histogram>
+
 <histogram name="Suggestions.FailedRequestErrorCode" enum="NetErrorCodes"
     expires_after="2020-02-23">
   <owner>mathp@chromium.org</owner>
@@ -15716,7 +15763,7 @@
 </histogram>
 
 <histogram name="Tablet.AppDrag.EndWindowState"
-    enum="AppWindowDragEndWindowState" expires_after="2021-06-01">
+    enum="AppWindowDragEndWindowState" expires_after="2021-08-09">
   <owner>minch@chromium.org</owner>
   <owner>omrilio@chromium.org</owner>
   <summary>
@@ -16668,7 +16715,7 @@
 </histogram>
 
 <histogram name="Tracing.Background.FinalizationDisallowedReason"
-    enum="TracingFinalizationDisallowedReason" expires_after="2021-06-06">
+    enum="TracingFinalizationDisallowedReason" expires_after="2021-08-09">
   <owner>ssid@chromium.org</owner>
   <summary>
     Reason why background tracing finalization was not allowed. Also see
@@ -16839,7 +16886,7 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.SplashScreenShown" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -17073,7 +17120,7 @@
 </histogram>
 
 <histogram name="UserManager.LoginUserType" enum="UserType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>achuith@chromium.org</owner>
   <summary>
     The number of users of different types that log in to the system (Chrome
@@ -17590,7 +17637,7 @@
 </histogram>
 
 <histogram name="Webapp.AddToHomescreenDialog.Timeout" units="ms"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>dominickn@chromium.org</owner>
   <owner>peconn@chromium.org</owner>
   <summary>
@@ -17734,7 +17781,7 @@
 </histogram>
 
 <histogram name="WebApp.InstallIphPromo.Result" enum="WebAppInstallIphResult"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>phillis@chromium.org</owner>
   <owner>dmurph@chromium.org</owner>
   <summary>
@@ -17785,7 +17832,7 @@
 </histogram>
 
 <histogram name="WebApp.Launcher.LaunchResult"
-    enum="WebAppLauncherLaunchResult" expires_after="2021-04-10">
+    enum="WebAppLauncherLaunchResult" expires_after="2021-08-09">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
@@ -17795,7 +17842,7 @@
 </histogram>
 
 <histogram name="WebApp.Launcher.UpdateResult"
-    enum="WebAppLauncherUpdateResult" expires_after="2021-04-10">
+    enum="WebAppLauncherUpdateResult" expires_after="2021-08-09">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
@@ -17823,7 +17870,7 @@
 </histogram>
 
 <histogram name="WebApp.Mover.Result" enum="WebAppMoverResult"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>dmurph@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
@@ -17959,7 +18006,7 @@
 </histogram>
 
 <histogram name="Webapp.Update.ManifestUpdateResult"
-    enum="WebAppManifestUpdateResult" expires_after="M92">
+    enum="WebAppManifestUpdateResult" expires_after="2021-08-09">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@chromium.org</owner>
   <owner>loyso@chromium.org</owner>
@@ -18009,7 +18056,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.CableV1DiscoveryEvent"
-    enum="WebAuthenticationCableV1DiscoveryEvent" expires_after="2021-06-06">
+    enum="WebAuthenticationCableV1DiscoveryEvent" expires_after="2021-08-09">
   <owner>agl@chromium.org</owner>
   <owner>martinkr@google.com</owner>
   <summary>
@@ -18212,7 +18259,7 @@
 </histogram>
 
 <histogram name="WebController.ExternalURLRequestBlocking"
-    enum="IOSExternalURLRequestStatus" expires_after="2021-04-25">
+    enum="IOSExternalURLRequestStatus" expires_after="2021-08-09">
   <owner>mrefaat@chromium.org</owner>
   <summary>
     [iOS] Measures the proportion of external URL requests that originate from a
@@ -18431,7 +18478,7 @@
 </histogram>
 
 <histogram name="WebShare.ApiCount" enum="WebShareMethod"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mgiuca@chromium.org</owner>
   <summary>
     Counts the number of calls to navigator.share. Includes both successful and
@@ -18475,7 +18522,7 @@
 </histogram>
 
 <histogram name="WebsiteSettings.AllSitesAction2"
-    enum="WebSiteSettingsAllSitesAction2" expires_after="2021-04-18">
+    enum="WebSiteSettingsAllSitesAction2" expires_after="2021-08-09">
   <owner>jarrydg@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
   <summary>
@@ -18629,7 +18676,7 @@
 </histogram>
 
 <histogram name="WebUI.Settings.PathVisited" enum="WebUISettingsPathHashes"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>dschuyler@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <owner>bettes@chromium.org</owner>
@@ -18653,7 +18700,7 @@
 </histogram>
 
 <histogram name="WebUITabStrip.CloseTabAction"
-    enum="WebUITabStripCloseTabActions" expires_after="2021-06-06">
+    enum="WebUITabStripCloseTabActions" expires_after="2021-08-09">
   <owner>johntlee@chromium.org</owner>
   <owner>dpapad@chromium.org</owner>
   <summary>
@@ -18675,7 +18722,7 @@
 </histogram>
 
 <histogram name="WebUITabStrip.OpenDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>collinbaker@chromium.org</owner>
   <owner>dfried@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index ce522962..ce2d5dd 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -143,7 +143,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.AllPages.PercentNetworkBytesAds"
-    units="%" expires_after="2021-05-30">
+    units="%" expires_after="2021-08-09">
   <owner>alexmt@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -169,7 +169,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.FrameCounts.IgnoredByRestrictedAdTagging"
-    enum="BooleanIgnored" expires_after="2021-04-04">
+    enum="BooleanIgnored" expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -209,7 +209,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.HeavyAds.IgnoredByReload"
-    enum="BooleanIgnored" expires_after="2021-03-28">
+    enum="BooleanIgnored" expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -223,7 +223,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.HeavyAds.NetworkBytesAtFrameUnload"
-    units="bytes" expires_after="2021-04-04">
+    units="bytes" expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>justinmiron@google.com</owner>
   <summary>
@@ -235,7 +235,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.HeavyAds.UserDidReload"
-    enum="BooleanReloaded" expires_after="2021-05-30">
+    enum="BooleanReloaded" expires_after="2021-08-09">
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -251,6 +251,7 @@
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     The maximum reported number of kilobytes of memory used by V8 by the main
     frame in this pageload.
@@ -264,9 +265,15 @@
 
 <histogram name="PageLoad.Clients.Ads.Memory.MissedMeasurementCount"
     units="count" expires_after="2021-04-18">
+  <obsolete>
+    Removed 02/2021. Seen to be ~0.04 (sample mean) compared to a sample mean
+    UpdateCount of ~58.38 measured over all OSes for Dev and Canary for ending
+    date 10/28/2020 with 7-day aggregation.
+  </obsolete>
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     The number of V8 per-frame memory measurements received by
     AdsPageLoadMetricsObserver after the corresponding RenderFrameHost has
@@ -280,6 +287,7 @@
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     The number of V8 memory measurement updates received by
     AdsPageLoadMetricsObserver per pageload. Only recorded if the page has at
@@ -814,7 +822,7 @@
 
 <histogram
     name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -824,7 +832,7 @@
 </histogram>
 
 <histogram name="PageLoad.DocumentTiming.NavigationToLoadEventFired" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -834,7 +842,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Background"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -845,7 +853,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Close"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -856,7 +864,7 @@
 
 <histogram base="true"
     name="PageLoad.Experimental.AbortTiming.ForwardBackNavigation" units="ms"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -866,7 +874,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.NewNavigation"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -886,7 +894,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Reload"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -896,7 +904,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Stop" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -918,7 +926,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.Bytes.Network" units="KB"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The number of prefiltered (e.g., compressed) response body KiloBytes loaded
@@ -929,7 +937,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.Bytes.NetworkIncludingHeaders"
-    units="KB" expires_after="2021-06-06">
+    units="KB" expires_after="2021-08-09">
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The number of prefiltered (e.g., compressed) KiloBytes loaded over the
@@ -964,7 +972,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.ClickInputBurst" units="count"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>dougarnett@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <owner>sullivan@chromium.org</owner>
@@ -1221,7 +1229,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.PageLoadType" enum="PageLoadType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -1246,7 +1254,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.PaintTiming.InputToFirstContentfulPaint"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>sullivan@chromium.org</owner>
   <summary>
     The time between the OS-level input event that initiated a navigation, and
@@ -1287,7 +1295,7 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>ksakamoto@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -1311,7 +1319,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.TotalForegroundDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -1598,7 +1606,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"
-    enum="Boolean" expires_after="2021-04-18">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <summary>
     Counts how often a client-side redirect was initiated from a page that did
@@ -1619,7 +1627,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.NavigationStartedInForeground"
-    enum="BooleanForeground" expires_after="2021-06-06">
+    enum="BooleanForeground" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <summary>Whether a navigation started in the foreground.</summary>
 </histogram>
@@ -1691,7 +1699,7 @@
 
 <histogram
     name="PageLoad.Internal.PaintTiming.LargestContentfulPaint.ContentType"
-    enum="LargestContentType" expires_after="2021-06-06">
+    enum="LargestContentType" expires_after="2021-08-09">
   <owner>maxlg@chromium.org</owner>
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -1705,7 +1713,7 @@
 
 <histogram
     name="PageLoad.Internal.PaintTiming.LargestContentfulPaint.MainFrame.ContentType"
-    enum="LargestContentType" expires_after="2021-04-04">
+    enum="LargestContentType" expires_after="2021-08-09">
   <owner>maxlg@chromium.org</owner>
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -1829,9 +1837,12 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore"
-    units="scorex10" expires_after="2021-08-08">
+    units="scorex10" expires_after="never">
+<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
+
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
   <summary>
     Measures the cumulative layout shift score (bit.ly/lsm-explainer) that has
     occurred on the page (including all subframes). Recorded at the end of the
@@ -1839,6 +1850,9 @@
     tab is being closed. Stable since M79; previous versions are expermental and
     subject to fluctuation between releases.
 
+    This histogram is of special interest to the chrome-analysis-team@. Do not
+    change its semantics or retire it without talking to them first.
+
     Log of major changes: http://bit.ly/chrome-speed-metrics-changelog
   </summary>
 </histogram>
@@ -1858,7 +1872,7 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame"
-    units="scorex10" expires_after="2021-06-06">
+    units="scorex10" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
   <summary>
@@ -1883,10 +1897,11 @@
 </histogram>
 
 <histogram name="PageLoad.Memory.Aggregate.Max" units="KiB"
-    expires_after="2021-01-31">
+    expires_after="2021-07-31">
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     The maximum reported aggregate number of kilobytes of memory used by V8 by
     ad frames in this pageload. The value recorded is the maximum simultaneous
@@ -1900,10 +1915,11 @@
 </histogram>
 
 <histogram name="PageLoad.Memory.PerFrame.Max" units="KiB"
-    expires_after="2021-01-31">
+    expires_after="2021-07-31">
   <owner>cammie@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-ads-histograms@google.com</owner>
   <summary>
     The maximum reported number of kilobytes of memory used by V8 by frames in
     the ad frame tree in this pageload. The value recorded is the maximum
@@ -1926,7 +1942,7 @@
 </histogram>
 
 <histogram name="PageLoad.PageTiming.ForegroundDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <summary>
     For page loads that start in the foreground, measures the duration of time
@@ -1963,7 +1979,7 @@
 </histogram>
 
 <histogram name="PageLoad.PageTiming.NavigationToFirstForeground" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -1974,7 +1990,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.ForegroundToFirstContentfulPaint"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>sullivan@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2048,7 +2064,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToFirstImagePaint" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>sullivan@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2059,7 +2075,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToFirstPaint" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ksakamoto@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2130,7 +2146,7 @@
 
 <histogram
     name="PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -2171,7 +2187,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.ParseStartToFirstContentfulPaint"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -2182,7 +2198,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.NavigationToParseStart" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -2214,7 +2230,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.ParseBlockedOnScriptLoad" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/password/histograms.xml b/tools/metrics/histograms/histograms_xml/password/histograms.xml
index 86a1382..e825558 100644
--- a/tools/metrics/histograms/histograms_xml/password/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/password/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="PasswordBubble.DisplayDisposition"
-    enum="PasswordBubbleDisplayDisposition" expires_after="2021-06-06">
+    enum="PasswordBubbleDisplayDisposition" expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <summary>
     When the password management bubble opened, what state was it in?
@@ -50,7 +50,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.Event" enum="PasswordGenerationEvent"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
@@ -87,7 +87,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.PopupShown"
-    enum="PasswordGenerationPopupShown" expires_after="M92">
+    enum="PasswordGenerationPopupShown" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>kazinova@google.com</owner>
   <summary>Records an entry if (and only if) a popup was shown.</summary>
@@ -170,7 +170,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AccessPasswordInSettings"
-    enum="AccessPasswordInSettingsEvent" expires_after="M92">
+    enum="AccessPasswordInSettingsEvent" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -744,7 +744,7 @@
 </histogram>
 
 <histogram base="true" name="PasswordManager.BlacklistedSitesHiRes"
-    units="sites" expires_after="M92">
+    units="sites" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -776,7 +776,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.CanceledTime" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -785,7 +785,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.CheckedCredentials"
-    units="credentials" expires_after="2021-06-06">
+    units="credentials" expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -795,14 +795,14 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.Error"
-    enum="PasswordLeakDetectionError" expires_after="2021-06-06">
+    enum="PasswordLeakDetectionError" expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>Error encountered during the password bulk check.</summary>
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.LeaksFound" units="credentials"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -831,7 +831,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.Time" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -840,7 +840,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.TimePerCredential" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -850,14 +850,14 @@
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.UserAction"
-    enum="PasswordCheckInteraction" expires_after="2021-06-06">
+    enum="PasswordCheckInteraction" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>User actions performed on the Password Check settings page.</summary>
 </histogram>
 
 <histogram name="PasswordManager.BulkCheck.UserActionAndroid"
-    enum="PasswordCheckUIUserActionAndroid" expires_after="2021-06-06">
+    enum="PasswordCheckUIUserActionAndroid" expires_after="2021-08-09">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschigg@chromium.org</owner>
   <summary>User actions performed on the Password Check settings view.</summary>
@@ -1098,7 +1098,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FillingAssistance"
-    enum="PasswordManagerFillingAssistance" expires_after="2021-06-06">
+    enum="PasswordManagerFillingAssistance" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1144,7 +1144,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FirstRendererFillingResult"
-    enum="PasswordManagerFirstRendererFillingResult" expires_after="M92">
+    enum="PasswordManagerFirstRendererFillingResult" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1157,7 +1157,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FirstWaitForUsernameReason"
-    enum="PasswordManagerFirstWaitForUsernameReason" expires_after="M92">
+    enum="PasswordManagerFirstWaitForUsernameReason" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1348,7 +1348,7 @@
 </histogram>
 
 <histogram name="PasswordManager.JavaScriptOnlyValueInSubmittedForm"
-    enum="JavaScriptOnlyValueInPasswordForm" expires_after="M92">
+    enum="JavaScriptOnlyValueInPasswordForm" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1358,7 +1358,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.AccessTokenFetchStatus"
-    enum="GoogleServiceAuthError" expires_after="M92">
+    enum="GoogleServiceAuthError" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1368,7 +1368,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.AccessTokenNetErrorCode"
-    enum="NetErrorCodes" expires_after="M92">
+    enum="NetErrorCodes" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1378,21 +1378,22 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.AnalyzeSingleLeakResponseResult"
-    enum="PasswordAnalyzeLeakResponseResult" expires_after="M92">
+    enum="PasswordAnalyzeLeakResponseResult" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>Result of analyzing a single leak response.</summary>
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.AnalyzeSingleLeakResponseTime"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>The time it took to analyze a single leak lookup response.</summary>
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.DialogDismissalReason"
-    enum="PasswordLeakDetectionDialogDismissalReason" expires_after="M92">
+    enum="PasswordLeakDetectionDialogDismissalReason"
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1421,7 +1422,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.HttpResponseCode"
-    enum="HttpResponseCode" expires_after="M92">
+    enum="HttpResponseCode" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1431,7 +1432,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.IsPasswordReused" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -1441,7 +1442,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.IsPasswordSaved" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -1460,7 +1461,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.LookupSingleLeakResponseResult"
-    enum="PasswordLeakLookupResponseResult" expires_after="M92">
+    enum="PasswordLeakLookupResponseResult" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1480,7 +1481,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.NotifyIsLeakedTime" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1490,7 +1491,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.ObtainAccessTokenTime"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1500,7 +1501,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.PrepareSingleLeakRequestTime"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1509,7 +1510,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.ReceiveSingleLeakResponseTime"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1519,7 +1520,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.SingleLeakResponsePrefixes"
-    units="prefixes" expires_after="M92">
+    units="prefixes" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1529,7 +1530,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LeakDetection.SingleLeakResponseSize"
-    units="bytes" expires_after="M92">
+    units="bytes" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1546,7 +1547,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ManagePasswordsReferrer"
-    enum="ManagePasswordsReferrer" expires_after="M92">
+    enum="ManagePasswordsReferrer" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1705,7 +1706,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordDropdownItemSelected"
-    enum="PasswordDropdownSelectedOption" expires_after="2021-06-06">
+    enum="PasswordDropdownSelectedOption" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1714,14 +1715,14 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordDropdownShown"
-    enum="PasswordDropdownState" expires_after="2021-06-06">
+    enum="PasswordDropdownState" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>Logs the state of the password dropdown when it's shown.</summary>
 </histogram>
 
 <histogram name="PasswordManager.PasswordEditUpdatedValues"
-    enum="PasswordEditUpdatedValues" expires_after="M92">
+    enum="PasswordEditUpdatedValues" expires_after="2021-08-09">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1808,7 +1809,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordScriptsFetcher.CacheState"
-    enum="PasswordScriptsFetcherCacheState" expires_after="M92">
+    enum="PasswordScriptsFetcherCacheState" expires_after="2021-08-09">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1819,7 +1820,7 @@
 
 <histogram
     name="PasswordManager.PasswordScriptsFetcher.HttpResponseAndNetErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M92">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2021-08-09">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1829,14 +1830,14 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordScriptsFetcher.ParsingResult"
-    enum="PasswordScriptsFetcherParsingResult" expires_after="M92">
+    enum="PasswordScriptsFetcherParsingResult" expires_after="2021-08-09">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>Result of parsing of a list of available password scripts.</summary>
 </histogram>
 
 <histogram name="PasswordManager.PasswordScriptsFetcher.ResponseTime"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2021-08-09">
   <owner>kolos@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1869,7 +1870,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordSyncState" enum="PasswordSyncState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1912,7 +1913,7 @@
 </histogram>
 
 <histogram name="PasswordManager.RemoveCompromisedCredentials"
-    enum="PasswordStoreChange" expires_after="2021-06-06">
+    enum="PasswordStoreChange" expires_after="2021-08-09">
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1922,7 +1923,7 @@
 </histogram>
 
 <histogram name="PasswordManager.RemoveCompromisedCredentials.RemoveReason"
-    enum="RemoveCompromisedCredentialsReason" expires_after="2021-06-06">
+    enum="RemoveCompromisedCredentialsReason" expires_after="2021-08-09">
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1941,7 +1942,7 @@
 </histogram>
 
 <histogram name="PasswordManager.RequirementsSpecFetcher.NetErrorCode"
-    enum="NetErrorCodes" expires_after="2021-06-06">
+    enum="NetErrorCodes" expires_after="2021-08-09">
   <owner>battre@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -2002,7 +2003,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SaveUIDismissalReason"
-    enum="PasswordManagerUIDismissalReason" expires_after="2021-06-06">
+    enum="PasswordManagerUIDismissalReason" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="PasswordAccountStorageUserState" -->
 
   <owner>vasilii@chromium.org</owner>
@@ -2023,7 +2024,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SavingOnUsernameFirstFlow"
-    enum="SavingOnUsernameFirstFlow" expires_after="M92">
+    enum="SavingOnUsernameFirstFlow" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2034,7 +2035,7 @@
 </histogram>
 
 <histogram name="PasswordManager.StoreDecryptionResult"
-    enum="PasswordDecryptionResult" expires_after="2021-06-01">
+    enum="PasswordDecryptionResult" expires_after="2021-08-09">
   <owner>cfroussios@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -2200,7 +2201,7 @@
 </histogram>
 
 <histogram name="PasswordManager.TouchToFill.CredentialIndex" units="index"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
@@ -2210,7 +2211,7 @@
 </histogram>
 
 <histogram name="PasswordManager.TouchToFill.DismissalReason"
-    enum="BottomSheet.StateChangeReason" expires_after="2021-06-06">
+    enum="BottomSheet.StateChangeReason" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
@@ -2245,7 +2246,7 @@
 </histogram>
 
 <histogram name="PasswordManager.UpdateUIDismissalReason"
-    enum="PasswordManagerUIDismissalReason" expires_after="2021-06-06">
+    enum="PasswordManagerUIDismissalReason" expires_after="2021-08-09">
   <owner>vasilii@chromium.org</owner>
   <summary>Why was the update password UI (bubble or infobar) closed?</summary>
 </histogram>
@@ -2281,7 +2282,7 @@
 </histogram>
 
 <histogram name="PasswordManager.WeakCheck.CheckedPasswords" units="passwords"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2290,7 +2291,7 @@
 </histogram>
 
 <histogram name="PasswordManager.WeakCheck.PasswordScore"
-    enum="PasswordWeaknessScore" expires_after="M92">
+    enum="PasswordWeaknessScore" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2299,14 +2300,15 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordManager.WeakCheck.Time" units="ms" expires_after="M92">
+<histogram name="PasswordManager.WeakCheck.Time" units="ms"
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>The time it took to complete the passwords weak check.</summary>
 </histogram>
 
 <histogram name="PasswordManager.WeakCheck.WeakPasswords" units="passwords"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2316,7 +2318,7 @@
 
 <histogram
     name="PasswordManager.WellKnownChangePassword.GetChangePasswordUsage"
-    enum="GetChangePasswordUrlMetric" expires_after="2021-06-06">
+    enum="GetChangePasswordUrlMetric" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2346,7 +2348,7 @@
 </histogram>
 
 <histogram name="PasswordManager.WellKnownChangePassword.GstaticFetchResult"
-    enum="ChangePasswordUrlFetchResult" expires_after="2021-06-06">
+    enum="ChangePasswordUrlFetchResult" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2356,7 +2358,7 @@
 </histogram>
 
 <histogram name="PasswordManager.WellKnownChangePassword.GstaticFetchTime"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>Logs the loading time for the gstatic file request.</summary>
@@ -2494,7 +2496,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.RequestNetworkDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/permissions/histograms.xml b/tools/metrics/histograms/histograms_xml/permissions/histograms.xml
index 513215c5..6aa69c1 100644
--- a/tools/metrics/histograms/histograms_xml/permissions/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/permissions/histograms.xml
@@ -181,7 +181,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.PreloadData.NotificationUxQuality"
-    enum="CrowdDenyNotificationUxQuality" expires_after="2021-06-06">
+    enum="CrowdDenyNotificationUxQuality" expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -214,7 +214,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.SafeBrowsing.RequestDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -226,7 +226,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.SafeBrowsing.Verdict"
-    enum="CrowdDenySafeBrowsingVerdict" expires_after="2021-06-06">
+    enum="CrowdDenySafeBrowsingVerdict" expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -512,7 +512,7 @@
 
 <histogram
     name="Permissions.QuietNotificationPrompts.DidEnableAdapativelyInPrefs"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -524,7 +524,7 @@
 
 <histogram
     name="Permissions.QuietNotificationPrompts.EnabledStateInPrefsChangedTo"
-    enum="BooleanEnabled" expires_after="2021-06-06">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -535,7 +535,7 @@
 </histogram>
 
 <histogram name="Permissions.QuietNotificationPrompts.IsEnabledInPrefs"
-    enum="BooleanEnabled" expires_after="2021-06-06">
+    enum="BooleanEnabled" expires_after="2021-08-09">
   <owner>andypaicu@chromium.org</owner>
   <owner>engedy@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/platform/histograms.xml b/tools/metrics/histograms/histograms_xml/platform/histograms.xml
index dfe3ec13a..30c3358 100644
--- a/tools/metrics/histograms/histograms_xml/platform/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/platform/histograms.xml
@@ -226,7 +226,7 @@
 </histogram>
 
 <histogram name="Platform.CrOS.CrashSenderRemoveReason"
-    enum="CrosCrashSenderRemoveReason" expires_after="2021-06-06">
+    enum="CrosCrashSenderRemoveReason" expires_after="2021-08-09">
   <owner>iby@chromium.org</owner>
   <owner>mutexlox@chromium.org</owner>
   <owner>cros-telemetry@google.com</owner>
@@ -360,7 +360,7 @@
 </histogram>
 
 <histogram name="Platform.DetachableBase.RWUpdateResult"
-    enum="DetachableBaseRWUpdateResult" expires_after="2021-06-02">
+    enum="DetachableBaseRWUpdateResult" expires_after="2021-08-09">
   <owner>drinkcat@chromium.org</owner>
   <owner>fshao@chromium.org</owner>
   <owner>chromeos-kukui@google.com</owner>
@@ -626,7 +626,7 @@
   </summary>
 </histogram>
 
-<histogram name="Platform.Memory.Gpu" units="MiB" expires_after="2021-06-06">
+<histogram name="Platform.Memory.Gpu" units="MiB" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
 
   <owner>sonnyrao@chromium.org</owner>
@@ -945,7 +945,7 @@
   </summary>
 </histogram>
 
-<histogram name="Platform.SwapInDaily" units="pages" expires_after="2021-04-28">
+<histogram name="Platform.SwapInDaily" units="pages" expires_after="2021-08-09">
   <owner>asavery@chromium.org</owner>
   <owner>chromeos-storage@google.com</owner>
   <summary>Number of pages swapped IN over a day, sampled daily.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/plugin/histograms.xml b/tools/metrics/histograms/histograms_xml/plugin/histograms.xml
index f084f2f..907b42a8 100644
--- a/tools/metrics/histograms/histograms_xml/plugin/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/plugin/histograms.xml
@@ -28,7 +28,7 @@
 </histogram>
 
 <histogram name="Plugin.FlashUsage" enum="FlashUsage"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yzshen@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>Collects Flash usage data.</summary>
@@ -132,7 +132,7 @@
 </histogram>
 
 <histogram name="PluginVm.EngagementTime.Background" units="ms"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>timloh@google.com</owner>
   <owner>joelhockey@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/power/histograms.xml b/tools/metrics/histograms/histograms_xml/power/histograms.xml
index ad14612..9c8789a 100644
--- a/tools/metrics/histograms/histograms_xml/power/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/power/histograms.xml
@@ -64,7 +64,7 @@
 </histogram>
 
 <histogram name="Power.BatteryChargeHealth" units="%"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS battery charge health percentage. Sampled once when device starts
@@ -82,7 +82,7 @@
 </histogram>
 
 <histogram name="Power.BatteryDischargeRateWhileSuspended" units="mW"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS battery discharge rate in mW while the system was suspended,
@@ -102,7 +102,7 @@
   </summary>
 </histogram>
 
-<histogram name="Power.BatteryPercentDrop" units="%" expires_after="2021-06-06">
+<histogram name="Power.BatteryPercentDrop" units="%" expires_after="2021-08-09">
   <owner>ryansturm@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -154,7 +154,7 @@
 </histogram>
 
 <histogram name="Power.BatteryRemainingWhenChargeStarts" units="%"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS remaining battery charge as percent of the maximum battery charge,
@@ -243,7 +243,7 @@
 </histogram>
 
 <histogram name="Power.CpuTimeSecondsPerThreadType"
-    enum="CpuTimeMetricsThreadType" expires_after="2021-06-10">
+    enum="CpuTimeMetricsThreadType" expires_after="2021-08-09">
   <owner>eseckler@chromium.org</owner>
   <owner>skyostil@chromium.org</owner>
   <summary>
@@ -593,7 +593,7 @@
 </histogram>
 
 <histogram name="Power.IdleSuspendCountDaily" units="count"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
@@ -720,7 +720,7 @@
 </histogram>
 
 <histogram name="Power.LidClosedSuspendCountDaily" units="count"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Number of times that that the system has suspended in response to its lid
@@ -871,7 +871,7 @@
 </histogram>
 
 <histogram name="Power.PowerButtonPressInTabletMode"
-    enum="PowerButtonPressType" expires_after="2021-06-06">
+    enum="PowerButtonPressType" expires_after="2021-08-09">
   <owner>minch@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -930,7 +930,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttempt" enum="SuspendAttempt"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts on Chrome OS. Samples are reported before
@@ -940,7 +940,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttemptsBeforeCancel" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts performed for a single suspend request (e.g.
@@ -951,7 +951,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttemptsBeforeSuccess" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts performed for a single suspend request (e.g.
@@ -961,7 +961,7 @@
 </histogram>
 
 <histogram name="Power.SuspendResult" enum="SuspendResult"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The results of suspend attempts on Chrome OS. Samples are reported after
@@ -1060,7 +1060,7 @@
 
 <histogram name="PowerML.SmartDimComponent.LoadComponentEvent"
     enum="PowerMLSmartDimComponentLoadComponentEvent"
-    expires_after="2021-04-25">
+    expires_after="2021-08-09">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>napper@chromium.org</owner>
@@ -1071,7 +1071,7 @@
 </histogram>
 
 <histogram name="PowerML.SmartDimComponent.VersionType"
-    enum="PowerMLSmartDimComponentVersionType" expires_after="2021-04-25">
+    enum="PowerMLSmartDimComponentVersionType" expires_after="2021-08-09">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>napper@chromium.org</owner>
@@ -1082,7 +1082,7 @@
 </histogram>
 
 <histogram name="PowerML.SmartDimComponent.WorkerType"
-    enum="PowerMLSmartDimComponentWorkerType" expires_after="2021-04-25">
+    enum="PowerMLSmartDimComponentWorkerType" expires_after="2021-08-09">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>napper@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/print/histograms.xml b/tools/metrics/histograms/histograms_xml/print/histograms.xml
index 341a93f..f8ffcee6 100644
--- a/tools/metrics/histograms/histograms_xml/print/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/print/histograms.xml
@@ -225,7 +225,7 @@
 </histogram>
 
 <histogram name="PrintPreview.UserAction" enum="PrintPreviewUserActionType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>thestig@chromium.org</owner>
   <owner>awscreen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/printing/histograms.xml b/tools/metrics/histograms/histograms_xml/printing/histograms.xml
index 6b51e4c3..a96dab4 100644
--- a/tools/metrics/histograms/histograms_xml/printing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/printing/histograms.xml
@@ -199,7 +199,7 @@
 </histogram>
 
 <histogram name="Printing.CUPS.MigratedMakeAndModel" enum="BooleanMigrated"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>luum@chromium.org</owner>
   <owner>cros-printing-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/profile/histograms.xml b/tools/metrics/histograms/histograms_xml/profile/histograms.xml
index 1350583..b538945 100644
--- a/tools/metrics/histograms/histograms_xml/profile/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/profile/histograms.xml
@@ -52,7 +52,7 @@
 </histogram>
 
 <histogram name="Profile.AllAccounts.Names" enum="ProfileAllAccountsNames"
-    expires_after="2021-05-16">
+    expires_after="2021-08-09">
   <owner>jkrcal@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -129,7 +129,7 @@
 </histogram>
 
 <histogram name="Profile.CreateResult" enum="ProfileCreateResult"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>treib@chromium.org</owner>
   <owner>rogerta@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
@@ -229,7 +229,7 @@
 </histogram>
 
 <histogram name="Profile.Guest.ForcedByPolicy" enum="BooleanForced"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
@@ -308,7 +308,7 @@
 </histogram>
 
 <histogram name="Profile.Menu.OpenedAfterAvatarAnimation" units="ms"
-    expires_after="2021-04-15">
+    expires_after="2021-08-09">
   <owner>droger@chromium.org</owner>
   <owner>jkrcal@chromium.org</owner>
   <summary>
@@ -584,7 +584,7 @@
 </histogram>
 
 <histogram name="Profile.SyncCustomize" enum="ProfileSyncCustomize"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>msalama@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -596,7 +596,7 @@
 </histogram>
 
 <histogram name="Profile.TimeToOpenUserManagerUpTo1min" units="ms"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>msarda@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/quota/histograms.xml b/tools/metrics/histograms/histograms_xml/quota/histograms.xml
index 136885f..d1f3f1d6 100644
--- a/tools/metrics/histograms/histograms_xml/quota/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/quota/histograms.xml
@@ -139,7 +139,7 @@
 </histogram>
 
 <histogram name="Quota.GlobalUsageOfTemporaryStorage" units="MB"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jarrydg@chromium.org</owner>
   <summary>Global usage of temporary storage.</summary>
 </histogram>
@@ -246,7 +246,7 @@
   <summary>Time spent to an eviction round.</summary>
 </histogram>
 
-<histogram name="Quota.TotalDiskSpace" units="MB" expires_after="2021-06-06">
+<histogram name="Quota.TotalDiskSpace" units="MB" expires_after="2021-08-09">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Total disk space for the storage directory. Logged at irregular intervals.
diff --git a/tools/metrics/histograms/histograms_xml/renderer/histograms.xml b/tools/metrics/histograms/histograms_xml/renderer/histograms.xml
index 3501550e..7d626fcf 100644
--- a/tools/metrics/histograms/histograms_xml/renderer/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/renderer/histograms.xml
@@ -200,7 +200,7 @@
 </histogram>
 
 <histogram base="true" name="RendererScheduler.QueueingDurationPerQueueType"
-    units="ms" expires_after="2021-06-06">
+    units="ms" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="RendererScheduler.QueueType" -->
 
   <owner>kdillon@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/renderer4/histograms.xml b/tools/metrics/histograms/histograms_xml/renderer4/histograms.xml
index 59a2585..a7bf634d 100644
--- a/tools/metrics/histograms/histograms_xml/renderer4/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/renderer4/histograms.xml
@@ -36,7 +36,7 @@
 </histogram>
 
 <histogram name="Renderer4.Browser.RasterTaskTotalDuration"
-    units="microseconds" expires_after="2021-06-01">
+    units="microseconds" expires_after="2021-08-09">
   <owner>khushalsagar@chromium.org</owner>
   <owner>chrome-gpu@google.com</owner>
   <summary>
@@ -71,7 +71,7 @@
 </histogram>
 
 <histogram name="Renderer4.GpuImageDecodeState" enum="GpuImageUsageState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cblume@chromium.org</owner>
   <owner>vmpstr@chromium.org</owner>
   <summary>
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="Renderer4.GpuImageDecodeState.CachePeakUsagePercent"
-    units="units" expires_after="2021-06-06">
+    units="units" expires_after="2021-08-09">
   <owner>sashamcintosh@chromium.org</owner>
   <owner>ericrk@chromium.org</owner>
   <summary>
@@ -158,7 +158,7 @@
 </histogram>
 
 <histogram base="true" name="Renderer4.ImageUploadTaskDurationUs"
-    units="microseconds" expires_after="2021-05-30">
+    units="microseconds" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="DecodedImageType" -->
 
   <owner>sashamcintosh@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
index e8899f8d..4931119 100644
--- a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
@@ -78,7 +78,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.BlockingPage.RequestDestination"
-    enum="RequestDestination" expires_after="2021-05-30">
+    enum="RequestDestination" expires_after="2021-08-09">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -694,7 +694,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.FileTypeUpdate.DynamicUpdateVersion"
-    units="FileTypePolicies Version" expires_after="2021-04-13">
+    units="FileTypePolicies Version" expires_after="2021-08-09">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -822,7 +822,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.Enhanced" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -832,7 +832,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.Extended" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -842,7 +842,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.General" enum="BooleanEnabled"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1187,7 +1187,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.TokenFetcher.ErrorType"
-    enum="GoogleServiceAuthError" expires_after="2021-06-06">
+    enum="GoogleServiceAuthError" expires_after="2021-08-09">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1382,7 +1382,7 @@
 
 <histogram
     name="SafeBrowsing.V4LocalDatabaseManager.TimeSinceLastUpdateResponse"
-    units="ms" expires_after="2021-05-09">
+    units="ms" expires_after="2021-08-09">
   <owner>ajuma@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/sb_client/histograms.xml b/tools/metrics/histograms/histograms_xml/sb_client/histograms.xml
index 7211130..cfe1786 100644
--- a/tools/metrics/histograms/histograms_xml/sb_client/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sb_client/histograms.xml
@@ -630,7 +630,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.VisualFeatureTime" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/search/histograms.xml b/tools/metrics/histograms/histograms_xml/search/histograms.xml
index e94e728..d97c776ec 100644
--- a/tools/metrics/histograms/histograms_xml/search/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/search/histograms.xml
@@ -214,7 +214,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearch.ResolveRequested"
-    enum="ContextualSearchGestureIsTap" expires_after="M91">
+    enum="ContextualSearchGestureIsTap" expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -225,7 +225,7 @@
 </histogram>
 
 <histogram name="Search.ContextualSearch.SelectionExpanded"
-    enum="ContextualSearchGestureIsTap" expires_after="M92">
+    enum="ContextualSearchGestureIsTap" expires_after="2021-08-09">
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -1288,7 +1288,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.FetcherNetErrorCode" enum="NetErrorCodes"
-    expires_after="M91">
+    expires_after="2021-08-09">
   <owner>hesen@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>Records the net error code get from TileFetcher.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/security/histograms.xml b/tools/metrics/histograms/histograms_xml/security/histograms.xml
index 0760514..7cd53b77 100644
--- a/tools/metrics/histograms/histograms_xml/security/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/security/histograms.xml
@@ -52,7 +52,7 @@
 </histogram>
 
 <histogram name="Security.LegacyTLS.DownloadStarted" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -66,7 +66,7 @@
 </histogram>
 
 <histogram name="Security.LegacyTLS.FormSubmission" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -78,7 +78,7 @@
 </histogram>
 
 <histogram name="Security.LegacyTLS.OnCommit" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
@@ -110,7 +110,7 @@
 </histogram>
 
 <histogram base="true" name="Security.PageEndReason" enum="PageEndReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -195,7 +195,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.TimeOpen.Action" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -205,7 +205,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.TimeOpen.NoAction" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -444,7 +444,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.CacheHWM" units="reports"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -456,7 +456,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportDeduplicated" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -467,7 +467,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSampled" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -478,7 +478,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSize" units="bytes"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -488,7 +488,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSucceeded" enum="Boolean"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -587,7 +587,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.OnCommit" enum="SecurityLevel"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -599,7 +599,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.OnComplete" enum="SecurityLevel"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -622,7 +622,7 @@
 </histogram>
 
 <histogram base="true" name="Security.SiteEngagementDelta" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -637,7 +637,7 @@
 </histogram>
 
 <histogram base="true" name="Security.TimeOnPage2" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/service/histograms.xml b/tools/metrics/histograms/histograms_xml/service/histograms.xml
index 1939c79..e5d2f42b 100644
--- a/tools/metrics/histograms/histograms_xml/service/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/service/histograms.xml
@@ -312,7 +312,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.FetchEvent.HasResponse.Time" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>falken@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -747,7 +747,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.ScriptCachedMetadataSize" units="bytes"
-    expires_after="2021-05-18">
+    expires_after="2021-08-09">
   <owner>falken@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1122,7 +1122,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.WorkerStopped" enum="ServiceWorkerStoppedStatus"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>falken@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/session/histograms.xml b/tools/metrics/histograms/histograms_xml/session/histograms.xml
index 72be82c..6b28698 100644
--- a/tools/metrics/histograms/histograms_xml/session/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/session/histograms.xml
@@ -465,7 +465,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.ReadFromFileTime" units="ms"
-    expires_after="2021-06-10">
+    expires_after="2021-08-09">
   <owner>justincohen@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <summary>
@@ -486,7 +486,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.SerializedSize" units="KB"
-    expires_after="2021-06-10">
+    expires_after="2021-08-09">
   <owner>justincohen@chromium.org</owner>
   <owner>rohitrao@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/settings/histograms.xml b/tools/metrics/histograms/histograms_xml/settings/histograms.xml
index 6ca0f9c..8518a27 100644
--- a/tools/metrics/histograms/histograms_xml/settings/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/settings/histograms.xml
@@ -60,7 +60,7 @@
 </histogram>
 
 <histogram name="Settings.Homepage.LocationType" enum="HomepageLocationType"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>bttk@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <owner>wenyufu@chromium.org</owner>
@@ -174,7 +174,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyCheck.ChromeCleanerResult"
-    enum="SafetyCheckChromeCleanerStatus" expires_after="M92">
+    enum="SafetyCheckChromeCleanerStatus" expires_after="2021-08-09">
   <owner>rainhard@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/sharing/histograms.xml b/tools/metrics/histograms/histograms_xml/sharing/histograms.xml
index 9745395..97b099c 100644
--- a/tools/metrics/histograms/histograms_xml/sharing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sharing/histograms.xml
@@ -218,7 +218,7 @@
 </histogram>
 
 <histogram name="Sharing.MessageReceivedType" enum="SharingMessageType"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/signin/histograms.xml b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
index ea42aaf..cbc416df 100644
--- a/tools/metrics/histograms/histograms_xml/signin/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
@@ -203,7 +203,7 @@
 </histogram>
 
 <histogram name="Signin.AuthError" enum="GoogleServiceAuthError"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>msarda@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -308,7 +308,7 @@
 </histogram>
 
 <histogram name="Signin.Extensions.GaiaRemoteConsentFlowResult"
-    enum="GaiaRemoteConsentFlowResult" expires_after="2021-03-28">
+    enum="GaiaRemoteConsentFlowResult" expires_after="2021-08-09">
   <owner>alexilin@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -318,7 +318,7 @@
 </histogram>
 
 <histogram name="Signin.Extensions.GetAuthTokenResult"
-    enum="GetAuthTokenResult" expires_after="2021-03-28">
+    enum="GetAuthTokenResult" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="GetAuthTokenType" -->
 
   <owner>alexilin@chromium.org</owner>
@@ -922,7 +922,7 @@
 </histogram>
 
 <histogram name="Signin.SecondaryAccountConsentLog"
-    enum="SecondaryAccountConsentLoggerResult" expires_after="2021-06-06">
+    enum="SecondaryAccountConsentLoggerResult" expires_after="2021-08-09">
   <owner>anastasiian@chromium.org</owner>
   <owner>sinhak@chromium.org</owner>
   <summary>
@@ -1178,7 +1178,7 @@
 </histogram>
 
 <histogram base="true" name="Signin.SyncErrorInfoBar"
-    enum="SyncErrorInfoBarAction" expires_after="2021-06-01">
+    enum="SyncErrorInfoBarAction" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="SyncErrorInfoBarType" -->
 
   <owner>triploblastic@chromium.org</owner>
@@ -1190,7 +1190,7 @@
 </histogram>
 
 <histogram name="Signin.SyncFirstSetupCompleteSource"
-    enum="SyncFirstSetupCompleteSource" expires_after="2021-06-01">
+    enum="SyncFirstSetupCompleteSource" expires_after="2021-08-09">
   <owner>triploblastic@chromium.org</owner>
   <owner>bsazonov@chromium.org</owner>
   <summary>Tracks where FirstSetupComplete bit is set from.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index 5d1f36a4..58b3b24 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -86,7 +86,7 @@
 </histogram>
 
 <histogram name="Stability.Android.RendererCrash" enum="Boolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>wnwen@chromium.org</owner>
   <summary>
     Counts renderer crashes including OOMs. Android only. Mirrors old stability
@@ -96,7 +96,7 @@
 
 <histogram name="Stability.Android.StrongBindingOomRemainingBindingState"
     enum="Android.ChildProcessBindingStateCombination"
-    expires_after="2021-04-25">
+    expires_after="2021-08-09">
   <owner>boliu@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -415,7 +415,7 @@
 </histogram>
 
 <histogram name="Stability.iOS.UTE.AvailableStorage" units="KB"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>michaeldo@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -441,7 +441,7 @@
 </histogram>
 
 <histogram name="Stability.iOS.UTE.DeviceThermalState"
-    enum="IOSDeviceThermalState" expires_after="2021-06-06">
+    enum="IOSDeviceThermalState" expires_after="2021-08-09">
   <owner>michaeldo@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -451,7 +451,7 @@
 </histogram>
 
 <histogram name="Stability.iOS.UTE.HasPossibleExplanation"
-    enum="BooleanHasPossibleExplanation" expires_after="2021-06-06">
+    enum="BooleanHasPossibleExplanation" expires_after="2021-08-09">
   <owner>michaeldo@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -508,7 +508,7 @@
 </histogram>
 
 <histogram name="Stability.iOS.UTE.OSRestartedAfterPreviousSession"
-    enum="BooleanRebooted" expires_after="2021-06-06">
+    enum="BooleanRebooted" expires_after="2021-08-09">
   <owner>olivierrobin@chromium.org</owner>
   <owner>michaeldo@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/startup/histograms.xml b/tools/metrics/histograms/histograms_xml/startup/histograms.xml
index 5b8314f..dd4f040 100644
--- a/tools/metrics/histograms/histograms_xml/startup/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/startup/histograms.xml
@@ -40,7 +40,7 @@
 </histogram>
 
 <histogram name="Startup.Android.CachedFeedVisibilityConsistency"
-    enum="BooleanConsistent" expires_after="2021-06-10">
+    enum="BooleanConsistent" expires_after="2021-08-09">
   <owner>hanxi@chromium.org</owner>
   <owner>spdonghao@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
@@ -112,7 +112,7 @@
 </histogram>
 
 <histogram name="Startup.Android.FeedsLoadingPlaceholderShown.Instant"
-    units="ms" expires_after="2021-06-10">
+    units="ms" expires_after="2021-08-09">
   <owner>hanxi@chromium.org</owner>
   <owner>spdonghao@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
@@ -125,7 +125,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.FeedStreamCreatedTime" units="ms"
-    expires_after="2021-06-10">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="JavaStartMode" -->
 
   <owner>hanxi@chromium.org</owner>
@@ -139,7 +139,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.FirstDrawCompletedTime" units="ms"
-    expires_after="2021-06-10">
+    expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="JavaStartMode" -->
 
   <owner>hanxi@chromium.org</owner>
@@ -161,7 +161,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.SingleTabTitleAvailableTime"
-    units="ms" expires_after="2021-06-10">
+    units="ms" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="JavaStartMode" -->
 
   <owner>hanxi@chromium.org</owner>
@@ -377,6 +377,9 @@
 
 <histogram name="Startup.BrowserWindow.FirstPaint.CompositingEnded" units="ms"
     expires_after="2020-04-26">
+  <obsolete>
+    Removed 02/2021.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <owner>mblsha@yandex-team.ru</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
index a76e0c98..3057aec 100644
--- a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
@@ -243,7 +243,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed"
-    units="microseconds" expires_after="M92">
+    units="microseconds" expires_after="2021-08-09">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -349,7 +349,7 @@
 
 <histogram
     name="SubresourceFilter.MainFrameLoad.RulesetIsAvailableAnyActivationLevel"
-    enum="BooleanAvailable" expires_after="2021-05-16">
+    enum="BooleanAvailable" expires_after="2021-08-09">
   <owner>alexmt@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -464,7 +464,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.Disallowed"
-    units="resource loads" expires_after="M92">
+    units="resource loads" expires_after="2021-08-09">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -488,7 +488,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.MatchedRules"
-    units="resource loads" expires_after="M92">
+    units="resource loads" expires_after="2021-08-09">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -647,7 +647,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.Blink.Ineligibility"
-    enum="BlinkSubresourceRedirectIneligibility" expires_after="M92">
+    enum="BlinkSubresourceRedirectIneligibility" expires_after="2021-08-09">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -723,7 +723,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.ImageCompressionNotificationInfoBar"
-    enum="HttpsImageCompressionInfoBarAction" expires_after="M92">
+    enum="HttpsImageCompressionInfoBarAction" expires_after="2021-08-09">
   <owner>rajendrant@chromium.org</owner>
   <owner>src/components/data_reduction_proxy/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/sync/histograms.xml b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
index bfbece06..16e4342 100644
--- a/tools/metrics/histograms/histograms_xml/sync/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
@@ -98,7 +98,7 @@
 </histogram>
 
 <histogram name="Sync.BookmarksModelMetadataCorruptionReason"
-    enum="SyncBookmarkModelMetadataCorruptionReason" expires_after="M92">
+    enum="SyncBookmarkModelMetadataCorruptionReason" expires_after="2021-08-09">
   <owner>rushans@google.com</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -161,7 +161,7 @@
 </histogram>
 
 <histogram name="Sync.ConfigureDataTypes" enum="SyncModelTypes"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -186,7 +186,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.ConfigureTime_Initial" units="ms"
-    expires_after="2021-04-01">
+    expires_after="2021-08-09">
   <owner>victorvianna@google.com</owner>
   <owner>jkrcal@chromium.org</owner>
   <summary>
@@ -196,7 +196,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.ConfigureTime_Subsequent" units="ms"
-    expires_after="2021-04-01">
+    expires_after="2021-08-09">
   <owner>victorvianna@google.com</owner>
   <owner>jkrcal@chromium.org</owner>
   <summary>
@@ -233,7 +233,7 @@
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnNewPassphrase"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
-    expires_after="2021-05-09">
+    expires_after="2021-08-09">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -261,7 +261,7 @@
 
 <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
-    expires_after="2021-04-04">
+    expires_after="2021-08-09">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -271,7 +271,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.Crypto.NigoriKeyDerivationDuration"
-    units="ms" expires_after="2021-04-04">
+    units="ms" expires_after="2021-08-09">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -414,7 +414,7 @@
 </histogram>
 
 <histogram name="Sync.InitialState" enum="SyncInitialState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -589,7 +589,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.ModelTypeErrorSite"
-    enum="SyncModelTypeErrorSite" expires_after="2021-06-06">
+    enum="SyncModelTypeErrorSite" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="SyncModelType" -->
 
   <owner>jkrcal@chromium.org</owner>
@@ -623,7 +623,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.ModelTypeStoreCommitWriteBatchOutcome"
-    enum="LevelDBStatus" expires_after="2021-06-06">
+    enum="LevelDBStatus" expires_after="2021-08-09">
 <!-- Name completed by histogram_suffixes name="SyncModelType" -->
 
   <owner>qjw@chromium.org</owner>
@@ -979,7 +979,7 @@
 </histogram>
 
 <histogram name="Sync.SharingMessage.CommitResult"
-    enum="SyncSharingMessageCommitErrorCode" expires_after="2021-06-06">
+    enum="SyncSharingMessageCommitErrorCode" expires_after="2021-08-09">
   <owner>rushans@google.com</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -1100,7 +1100,7 @@
 </histogram>
 
 <histogram name="Sync.SyncEverything2" enum="Boolean"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -1159,7 +1159,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.UndecryptedEntitiesOnDataTypeDisabled"
-    units="sync entities" expires_after="2021-06-06">
+    units="sync entities" expires_after="2021-08-09">
   <owner>victorvianna@google.com</owner>
   <owner>treib@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/tab/histograms.xml b/tools/metrics/histograms/histograms_xml/tab/histograms.xml
index faa38c3..474a15a 100644
--- a/tools/metrics/histograms/histograms_xml/tab/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/tab/histograms.xml
@@ -250,7 +250,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.MemoryUsage.CompressedData.PerThumbnailKiB"
-    units="KB" expires_after="M92">
+    units="KB" expires_after="2021-08-09">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -260,7 +260,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.MemoryUsage.CompressedData.TotalKiB" units="KB"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -318,7 +318,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.VideoCaptureDuration" units="ms"
-    expires_after="M92">
+    expires_after="2021-08-09">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -608,7 +608,7 @@
 </histogram>
 
 <histogram name="TabGroups.SessionsPerGroup" units="sessions"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -651,7 +651,7 @@
 </histogram>
 
 <histogram name="TabGroups.UserGroupCount" units="groups"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -672,7 +672,7 @@
 </histogram>
 
 <histogram name="TabGroups.UserNamedGroupCount" units="groups"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -1248,7 +1248,7 @@
   </summary>
 </histogram>
 
-<histogram name="Tabs.CountAtStartup" units="tabs" expires_after="M92">
+<histogram name="Tabs.CountAtStartup" units="tabs" expires_after="2021-08-09">
   <owner>marq@chromium.org</owner>
   <summary>[Android and iOS] The number of tabs open at cold launch.</summary>
 </histogram>
@@ -2605,7 +2605,7 @@
 </histogram>
 
 <histogram name="TabStrip.TabCountOnPageLoad" units="tabs"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/translate/histograms.xml b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
index 85877894..1c7dde7 100644
--- a/tools/metrics/histograms/histograms_xml/translate/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
@@ -61,7 +61,7 @@
 </histogram>
 
 <histogram name="Translate.CLD3.LanguageDetected" enum="CLD3LanguageCode"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>frechette@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -71,7 +71,7 @@
 </histogram>
 
 <histogram name="Translate.CLD3.LanguagePercentage" units="%"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>frechette@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -99,7 +99,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.AlwaysTranslate"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -109,7 +109,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.MoreLanguages"
-    enum="CLD3LanguageCode" expires_after="2021-04-04">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -118,7 +118,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.NeverTranslate"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -128,7 +128,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.PageNotIn"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -147,7 +147,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.TranslationsPerPage"
-    units="translations" expires_after="2021-06-06">
+    units="translations" expires_after="2021-08-09">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -168,7 +168,7 @@
 </histogram>
 
 <histogram name="Translate.ContentLanguage" enum="TranslateLanguage"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -178,7 +178,7 @@
 </histogram>
 
 <histogram name="Translate.DeclineTranslate" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -188,7 +188,7 @@
 </histogram>
 
 <histogram name="Translate.DeclineTranslateCloseInfobar" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -212,7 +212,7 @@
 </histogram>
 
 <histogram name="Translate.ExplicitLanguageAsk.Event"
-    enum="TranslateExplicitAskPromptEventType" expires_after="2021-06-06">
+    enum="TranslateExplicitAskPromptEventType" expires_after="2021-08-09">
   <owner>yyushkina@google.com</owner>
   <owner>anthonyvd@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -223,7 +223,7 @@
 </histogram>
 
 <histogram name="Translate.ExplicitLanguageAsk.LanguageAdded"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>yyushkina@google.com</owner>
   <owner>anthonyvd@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -234,7 +234,7 @@
 </histogram>
 
 <histogram name="Translate.ExplicitLanguageAsk.LanguageRemoved"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>yyushkina@google.com</owner>
   <owner>anthonyvd@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -304,7 +304,7 @@
 </histogram>
 
 <histogram name="Translate.LanguageDetection.ContentLength" units="characters"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>sclittle@chromium.org</owner>
   <owner>megjablon@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
@@ -339,7 +339,7 @@
 </histogram>
 
 <histogram name="Translate.LanguageSettingsIsShown" enum="BooleanShown"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>frechette@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -382,7 +382,7 @@
 </histogram>
 
 <histogram name="Translate.MenuTranslation.IsAvailable" enum="BooleanAvailable"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>cuianthony@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -419,7 +419,7 @@
 </histogram>
 
 <histogram name="Translate.ModifyOriginalLang" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -429,7 +429,7 @@
 </histogram>
 
 <histogram name="Translate.ModifyTargetLang" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -439,7 +439,7 @@
 </histogram>
 
 <histogram name="Translate.NeverTranslateLang" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -449,7 +449,7 @@
 </histogram>
 
 <histogram name="Translate.NeverTranslateSite" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -472,7 +472,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.FinalSourceLanguage"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -486,7 +486,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.FinalState" enum="TranslateState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -500,7 +500,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.FinalTargetLanguage"
-    enum="CLD3LanguageCode" expires_after="2021-06-06">
+    enum="CLD3LanguageCode" expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -528,7 +528,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.InitialState" enum="TranslateState"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -572,7 +572,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.NumReversions" units="reversions"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -585,7 +585,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.NumTargetLanguageChanges"
-    units="target language changes" expires_after="2021-06-06">
+    units="target language changes" expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -598,7 +598,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.NumTranslations" units="translations"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -610,7 +610,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.Ranker.Decision"
-    enum="TranslateRankerDecision" expires_after="2021-06-06">
+    enum="TranslateRankerDecision" expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -623,7 +623,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.Ranker.Version" units="version"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -635,7 +635,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.TriggerDecision"
-    enum="TranslateTriggerDecision" expires_after="2021-06-06">
+    enum="TranslateTriggerDecision" expires_after="2021-08-09">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -754,7 +754,7 @@
 </histogram>
 
 <histogram name="Translate.RevertTranslation" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -799,7 +799,7 @@
 </histogram>
 
 <histogram name="Translate.TargetLanguage" enum="CLD3LanguageCode"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>yyushkina@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -809,7 +809,7 @@
 </histogram>
 
 <histogram name="Translate.TargetLanguage.Origin"
-    enum="TranslateTargetLanguageOrigin" expires_after="2021-06-06">
+    enum="TranslateTargetLanguageOrigin" expires_after="2021-08-09">
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -854,7 +854,7 @@
 </histogram>
 
 <histogram name="Translate.Translate.AMPCacheURL" enum="BooleanTranslate"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -1011,7 +1011,7 @@
 </histogram>
 
 <histogram name="Translate.UserActionDuration" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>kenjibaheux@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/uma/histograms.xml b/tools/metrics/histograms/histograms_xml/uma/histograms.xml
index af1e97c..42472eb0 100644
--- a/tools/metrics/histograms/histograms_xml/uma/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/uma/histograms.xml
@@ -79,7 +79,7 @@
 </histogram>
 
 <histogram name="UMA.EntropySourceType" enum="UmaEntropySourceType"
-    expires_after="2021-04-15">
+    expires_after="2021-08-09">
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -273,7 +273,7 @@
 </histogram>
 
 <histogram name="UMA.LowEntropySourceValue" units="units"
-    expires_after="2021-04-15">
+    expires_after="2021-08-09">
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
@@ -580,7 +580,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2021-08-01">
+    expires_after="2021-08-09">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/update_engine/histograms.xml b/tools/metrics/histograms/histograms_xml/update_engine/histograms.xml
index d88e83d..ffe6402 100644
--- a/tools/metrics/histograms/histograms_xml/update_engine/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/update_engine/histograms.xml
@@ -298,7 +298,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.RollbackTargetVersion"
-    enum="UpdateEngineChromeOsVersionPrefix" expires_after="2021-05-16">
+    enum="UpdateEngineChromeOsVersionPrefix" expires_after="2021-08-09">
   <owner>mpolzer@google.com</owner>
   <owner>managed-platforms@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/v8/histograms.xml b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
index d0df313a..5510e913c 100644
--- a/tools/metrics/histograms/histograms_xml/v8/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
@@ -163,7 +163,7 @@
 </histogram>
 
 <histogram name="V8.CompileScriptMicroSeconds.BackgroundThread"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>rmcilroy@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
@@ -180,7 +180,7 @@
 </histogram>
 
 <histogram name="V8.CompileScriptMicroSeconds.ConsumeCache"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
@@ -228,7 +228,7 @@
 </histogram>
 
 <histogram name="V8.CompileScriptMicroSeconds.NoCache.CacheTooCold"
-    units="microseconds" expires_after="2021-06-06">
+    units="microseconds" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
@@ -396,7 +396,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCBackgroundMarking" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCBackgroundMarking" units="ms" expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -424,7 +424,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCCompactor" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCCompactor" units="ms" expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Time spent in mark-sweep phase of GC.</summary>
@@ -440,7 +440,7 @@
 </histogram>
 
 <histogram name="V8.GCCompactorForeground" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -448,7 +448,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCFinalizeMC" units="ms" expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -489,7 +489,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Finish" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCFinalizeMC.Finish" units="ms" expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -499,7 +499,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Mark" units="ms" expires_after="2021-04-01">
+<histogram name="V8.GCFinalizeMC.Mark" units="ms" expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -552,7 +552,7 @@
 </histogram>
 
 <histogram name="V8.GCFinalizeMCReduceMemory" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -584,7 +584,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Time spent doing incremental marking steps during GC.</summary>
@@ -598,21 +598,21 @@
 </histogram>
 
 <histogram name="V8.GCIncrementalMarkingReason" enum="GarbageCollectionReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Reason an incremental marking was started in V8.</summary>
 </histogram>
 
 <histogram name="V8.GCIncrementalMarkingStart" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Time spent in starting incremental marking.</summary>
 </histogram>
 
 <histogram name="V8.GCIncrementalMarkingSum" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -644,7 +644,7 @@
 </histogram>
 
 <histogram name="V8.GCMarkCompactReason" enum="GarbageCollectionReason"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>ulan@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Reason a mark-compact garbage collection was started in V8.</summary>
@@ -660,14 +660,14 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCScavenger" units="ms" expires_after="2021-06-06">
+<histogram name="V8.GCScavenger" units="ms" expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Time spent in scavenging phase of GC.</summary>
 </histogram>
 
 <histogram name="V8.GCScavenger.ScavengeMain" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -700,7 +700,7 @@
 </histogram>
 
 <histogram name="V8.GCScavengerForeground" units="ms"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -815,7 +815,7 @@
 </histogram>
 
 <histogram name="V8.RegExpBacktracks" units="backtracks"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>jgruber@chromium.org</owner>
   <owner>mvstanton@chromium.org</owner>
   <summary>
@@ -1167,7 +1167,7 @@
 </histogram>
 
 <histogram name="V8.WasmMemoryAllocationResult" enum="WasmAllocationResult"
-    expires_after="2021-03-31">
+    expires_after="2021-08-09">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
@@ -1234,7 +1234,7 @@
 </histogram>
 
 <histogram name="V8.WasmModuleNumberOfCodeGCsTriggered" units="gcs"
-    expires_after="2021-03-31">
+    expires_after="2021-08-09">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/variations/histograms.xml b/tools/metrics/histograms/histograms_xml/variations/histograms.xml
index 070aba8..e6cd288 100644
--- a/tools/metrics/histograms/histograms_xml/variations/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/variations/histograms.xml
@@ -88,7 +88,7 @@
 </histogram>
 
 <histogram name="Variations.FirstRun.SeedFetchTime" units="ms"
-    expires_after="2021-04-15">
+    expires_after="2021-08-09">
   <owner>asvitkine@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
diff --git a/tools/metrics/histograms/histograms_xml/web_audio/histograms.xml b/tools/metrics/histograms/histograms_xml/web_audio/histograms.xml
index 20b9e2d..8081271 100644
--- a/tools/metrics/histograms/histograms_xml/web_audio/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/web_audio/histograms.xml
@@ -108,7 +108,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContextOptions.sampleRate" units="Hz"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>rtoy@chromium.org</owner>
   <owner>hongchan@chromium.org</owner>
   <summary>
@@ -119,7 +119,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContextOptions.sampleRateRatio" units="units"
-    expires_after="2021-04-18">
+    expires_after="2021-08-09">
   <owner>rtoy@chromium.org</owner>
   <owner>hongchan@chromium.org</owner>
   <summary>
@@ -142,7 +142,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioDestination.HardwareBufferSize" units="units"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>rtoy@chromium.org</owner>
   <owner>hongchan@chromium.org</owner>
   <summary>
@@ -152,7 +152,7 @@
 </histogram>
 
 <histogram name="WebAudio.Autoplay" enum="WebAudioAutoplayStatus"
-    expires_after="2021-06-01">
+    expires_after="2021-08-09">
   <owner>mlamouri@google.com</owner>
   <owner>media-dev@chromium.org</owner>
   <owner>rtoy@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/web_core/histograms.xml b/tools/metrics/histograms/histograms_xml/web_core/histograms.xml
index 8512a002..ba45de4 100644
--- a/tools/metrics/histograms/histograms_xml/web_core/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/web_core/histograms.xml
@@ -800,35 +800,35 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.Async.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2021-06-06">
+    enum="NotStreamingReason" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Reason for not streaming an async script.</summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Async.StartedStreaming" enum="BooleanStreamed"
-    expires_after="2021-06-06">
+    expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Whether an async script was streamed or not.</summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Deferred.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2021-06-06">
+    enum="NotStreamingReason" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Reason for not streaming a deferred script.</summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Deferred.StartedStreaming"
-    enum="BooleanStreamed" expires_after="2021-06-06">
+    enum="BooleanStreamed" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Whether a deferred script was streamed or not.</summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Other.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2021-06-06">
+    enum="NotStreamingReason" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
@@ -848,14 +848,14 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.ParsingBlocking.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2021-06-06">
+    enum="NotStreamingReason" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Reason for not streaming a parsing blocking script.</summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.ParsingBlocking.StartedStreaming"
-    enum="BooleanStreamed" expires_after="2021-06-06">
+    enum="BooleanStreamed" expires_after="2021-08-09">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>Whether a parsing blocking script was streamed or not.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml b/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
index dc9cebbb..87067654 100644
--- a/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
@@ -1071,7 +1071,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.TimeReceivingVideoRtpPacketsInSeconds" units="s"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>saza@chromium.org</owner>
   <summary>
     The amount of time between the arrival of the first and last video RTP
@@ -1178,7 +1178,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.AddIceCandidate"
-    enum="AddIceCandidateResult" expires_after="2021-05-09">
+    enum="AddIceCandidateResult" expires_after="2021-08-09">
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1223,7 +1223,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.ConnectionState"
-    enum="IceConnectionStates" expires_after="2021-06-06">
+    enum="IceConnectionStates" expires_after="2021-08-09">
   <owner>qingsi@google.com</owner>
   <owner>jeroendb@google.com</owner>
   <summary>
@@ -1438,7 +1438,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpFormatReceived"
-    enum="PeerConnectionSdpFormatReceived" expires_after="2021-06-06">
+    enum="PeerConnectionSdpFormatReceived" expires_after="2021-08-09">
   <owner>steveanton@chromium.org</owner>
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
@@ -1455,7 +1455,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpFormatReceivedAnswer"
-    enum="PeerConnectionSdpFormatReceived" expires_after="2021-06-06">
+    enum="PeerConnectionSdpFormatReceived" expires_after="2021-08-09">
   <owner>steveanton@chromium.org</owner>
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
@@ -3638,7 +3638,7 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.UploadFailureReason"
-    enum="WebRtcLoggingUploadFailureReason" expires_after="2021-06-01">
+    enum="WebRtcLoggingUploadFailureReason" expires_after="2021-08-09">
   <owner>guidou@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/windows/histograms.xml b/tools/metrics/histograms/histograms_xml/windows/histograms.xml
index 6d060f2..83501ddd 100644
--- a/tools/metrics/histograms/histograms_xml/windows/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/windows/histograms.xml
@@ -360,7 +360,7 @@
 </histogram>
 
 <histogram name="Windows.TmpFileDeleter.FailCount" units="files"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <owner>etiennep@chromium.org</owner>
   <summary>
@@ -374,7 +374,7 @@
 </histogram>
 
 <histogram name="Windows.TmpFileDeleter.SuccessCount" units="files"
-    expires_after="2021-05-02">
+    expires_after="2021-08-09">
   <owner>grt@chromium.org</owner>
   <owner>etiennep@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index cf7c6e731..4d56c4f 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -10565,6 +10565,12 @@
       different renderer process than the current document or not.
     </summary>
   </metric>
+  <metric name="IsExistingPartOfTabGroup" enum="Boolean">
+    <summary>
+      Marked true if the page was already part of a tab group when the
+      navigation committed.
+    </summary>
+  </metric>
   <metric name="IsSignedExchangeInnerResponse">
     <obsolete>
       Migrated to the PageLoad.SignedExchange event. The presence of that event
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 29462bda..0f2e3d8 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -76,4 +76,4 @@
 v8.browsing_mobile-future,"mythria@chromium.org, tmrts@chromium.org",Blink>JavaScript,,"2018,2019,2020,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy"
 v8.runtime_stats.top_25,"mythria@chromium.org, ulan@chromium.org",Blink>JavaScript,,"cold,hot,warm"
 views_perftests,tapted@chromium.org,Internals>Views,,
-webrtc,"qiangchen@chromium.org, mbonadei@chromium.org",Blink>WebRTC,http://bit.ly/webrtc-benchmark,"datachannel,getusermedia,insertableStreams,pauseplay,smoothness,stress,videoConstraints"
+webrtc,"qiangchen@chromium.org, mbonadei@chromium.org",Blink>WebRTC,http://bit.ly/webrtc-benchmark,"datachannel,getusermedia,insertableStreams,pauseplay,sdp,smoothness,stress,videoConstraints"
diff --git a/tools/perf/chrome_telemetry_build/BUILD.gn b/tools/perf/chrome_telemetry_build/BUILD.gn
index 99ec0639..8a7da08 100644
--- a/tools/perf/chrome_telemetry_build/BUILD.gn
+++ b/tools/perf/chrome_telemetry_build/BUILD.gn
@@ -8,8 +8,26 @@
   # Pull in enable_chrome_android_internal and public_android_sdk
   import("//build/config/android/config.gni")
 }
-if (is_chromeos_ash) {
+
+if (is_chromeos_device) {
   import("//build/config/chromeos/rules.gni")
+
+  generate_runner_script("cros_update_wrapper") {
+    generated_script = "$root_build_dir/bin/cros_update_wrapper"
+    deploy_chrome = true
+  }
+
+  if (cros_boards != "") {
+    foreach(_board, string_split(cros_boards, ":")) {
+      _board_underscore = string_replace(_board, "-", "_")
+      generate_runner_script("cros_update_wrapper_${_board_underscore}") {
+        generated_script =
+            "$root_build_dir/bin/cros_update_wrapper_${_board_underscore}"
+        deploy_chrome = true
+        override_board = _board
+      }
+    }
+  }
 }
 
 group("telemetry_chrome_test") {
@@ -65,6 +83,16 @@
     ]
   }
 
+  if (is_chromeos_device) {
+    data_deps += [ ":cros_update_wrapper" ]
+    if (cros_boards != "") {
+      foreach(_board, string_split(cros_boards, ":")) {
+        _board_underscore = string_replace(_board, "-", "_")
+        data_deps += [ ":cros_update_wrapper_${_board_underscore}" ]
+      }
+    }
+  }
+
   if (is_chromeos_ash && is_chromeos_device) {
     data_deps += [
       "//:chromiumos_preflight",
diff --git a/tools/perf/cli_tools/tbmv3/validators/simple_configs.pyl b/tools/perf/cli_tools/tbmv3/validators/simple_configs.pyl
index 00d7e3c..abc594f 100644
--- a/tools/perf/cli_tools/tbmv3/validators/simple_configs.pyl
+++ b/tools/perf/cli_tools/tbmv3/validators/simple_configs.pyl
@@ -21,4 +21,14 @@
       'console:error:network': 'console_error::network_errors',
     },
   },
+  'reported_by_page': {
+    'v2_metric': 'reportedByPageMetric',
+    'v3_metric': 'reported_by_page',
+    'histogram_mappings': {
+      # mappings are 'v2_histogram: 'v3_histogram'.
+      'reported_by_page:time_to_viewable': 'reported_by_page::time_to_viewable',
+      'reported_by_page:time_to_interactive': 'reported_by_page::time_to_interactive',
+      'reported_by_page:benchmark_time': 'reported_by_page::benchmark_time',
+    },
+  },
 }
diff --git a/tools/perf/cli_tools/tbmv3/validators/simple_validator.py b/tools/perf/cli_tools/tbmv3/validators/simple_validator.py
index 5042a08a..9ba4033 100644
--- a/tools/perf/cli_tools/tbmv3/validators/simple_validator.py
+++ b/tools/perf/cli_tools/tbmv3/validators/simple_validator.py
@@ -5,6 +5,7 @@
 Validates the rendering/frame_times metric.
 """
 
+import sys
 from cli_tools.tbmv3.validators import utils
 
 CONFIG_FORMAT = ('Config must contain "v2_metric", "v3_metric", and '
@@ -30,7 +31,7 @@
     msg = ('List of histograms produced by TBM%s metric %s:\n' %
            (version, metric))
     msg += '\n'.join([h.name for h in histogram_set])
-    raise Exception('Histgoram %s not found.\n%s' % (name, msg))
+    raise Exception('Histogram %s not found.\n%s' % (name, msg))
   if len(hists) > 1:
     raise Exception('Multiple histograms named %s found for TBM%s metric %s' %
                     (name, version, metric))
@@ -50,5 +51,11 @@
     v2_hist = GetHistogram(v2_histograms, v2_hist_name, v2_metric, 'v2')
     v3_hist = GetHistogram(v3_histograms, v3_hist_name, v3_metric, 'v3')
 
-    utils.AssertHistogramStatsAlmostEqual(test_ctx, v2_hist, v3_hist)
-    utils.AssertHistogramSamplesAlmostEqual(test_ctx, v2_hist, v3_hist)
+    try:
+      utils.AssertHistogramStatsAlmostEqual(test_ctx, v2_hist, v3_hist)
+      utils.AssertHistogramSamplesAlmostEqual(test_ctx, v2_hist, v3_hist)
+    except AssertionError as err:
+      message = (
+          'Error comparing TBMv2 histogram %s with TBMv3 histogram %s: %s' %
+          (v2_hist.name, v3_hist.name, err.message))
+      raise AssertionError, message, sys.exc_info()[2]
diff --git a/tools/perf/cli_tools/tbmv3/validators/utils.py b/tools/perf/cli_tools/tbmv3/validators/utils.py
index 99a6da7d..f0c7b0e 100644
--- a/tools/perf/cli_tools/tbmv3/validators/utils.py
+++ b/tools/perf/cli_tools/tbmv3/validators/utils.py
@@ -23,7 +23,5 @@
   test_ctx.assertEqual(len(v2_samples), len(v3_samples))
   v2_samples.sort()
   v3_samples.sort()
-  msg = ('Comparing TBMv2 histogram %s with TBMv3 hisogram %s.' %
-         (v2_hist.name, v3_hist.name))
   for v2_sample, v3_sample in zip(v2_samples, v3_samples):
-    test_ctx.assertAlmostEqual(v2_sample, v3_sample, places=3, msg=msg)
+    test_ctx.assertAlmostEqual(v2_sample, v3_sample, places=3)
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 80bce935..a89ee0f 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -463,14 +463,14 @@
 # Linux
 LINUX = PerfPlatform(
     'linux-perf',
-    'Ubuntu-14.04, 8 core, NVIDIA Quadro P400',
+    'Ubuntu-18.04, 8 core, NVIDIA Quadro P400',
     _LINUX_BENCHMARK_CONFIGS,
     26,
     'linux',
     executables=_LINUX_EXECUTABLE_CONFIGS)
 LINUX_REL = PerfPlatform(
     'linux-perf-rel',
-    'Ubuntu-14.04, 8 core, NVIDIA Quadro P400',
+    'Ubuntu-18.04, 8 core, NVIDIA Quadro P400',
     _CHROME_HEALTH_BENCHMARK_CONFIGS_DESKTOP,
     2,
     'linux',
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 0f6866a..7d2fa92 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -205,7 +205,9 @@
             'extra_args': [
                 'hardware_accelerated_feature', '--show-stdout',
                 '--browser=web-engine-shell', '--passthrough', '-v',
-                '--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc'
+                '--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc',
+                '--device=custom',
+                '--custom-device-target=internal.astro_target'
             ],
             'type':
             TEST_TYPES.GENERIC,
@@ -213,6 +215,7 @@
         'platform':
         'fuchsia',
         'dimension': {
+            'cpu': None,
             'device_type': 'Astro',
             'os': 'Fuchsia',
             'pool': 'chrome.tests',
@@ -279,7 +282,7 @@
         'tests': [
             {
                 'isolate':
-                'performance_test_suite',
+                'performance_test_suite_eve',
                 'extra_args': [
                     # The magic hostname that resolves to a CrOS device in the test lab
                     '--remote=variable_chromeos_device_hostname',
@@ -446,7 +449,7 @@
         'additional_compile_targets': ['chromedriver', 'chromium_builder_perf'],
     },
     'linux-builder-perf-rel': {
-        'additional_compile_targets': ['chromedriver', 'chromium_builder_perf'],
+        'additional_compile_targets': ['chromium_builder_perf'],
     },
     'mac-builder-perf': {
         'additional_compile_targets': ['chromedriver', 'chromium_builder_perf'],
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index e8ee1ea..2fdf9ee 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "05f898ec237791c9d9d6d1901e9cef62888eb944",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/64c64075bae21d3d7d37e56fdc71f57d2de9c712/trace_processor_shell.exe"
+            "hash": "172c53a6397bd548931b8f6dbc9abab3bf4db702",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/c0182a50034c0c17a444c40aec96ee54239bc269/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "e8bf46a9b6e7b5d5bb248ebc388e543d238a79dc",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/f69ae0481064617cba71c34be1570fa56b49d076/trace_processor_shell"
+            "hash": "b983f5d7f044777facbf785367a560e7d8b07c6c",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/c0182a50034c0c17a444c40aec96ee54239bc269/trace_processor_shell"
         },
         "linux": {
-            "hash": "88cccc124fe04445c17f1b2649ac1b7d44c53296",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/0ac08fa635ec4fb59dcde90542f7e850ec5fa010/trace_processor_shell"
+            "hash": "ecf01a77a074b6ac612cb19909eba5ea93d8cf75",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/c0182a50034c0c17a444c40aec96ee54239bc269/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
index 41a01f6..269476c 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
@@ -12,7 +12,7 @@
         "name": "blink_perf.accessibility/line-breaks.html"
     },
     {
-        "duration": "36.0",
+        "duration": "37.0",
         "name": "blink_perf.accessibility/many-text-changes-deep-block-subtree.html"
     },
     {
@@ -24,7 +24,7 @@
         "name": "blink_perf.accessibility/many-text-changes-deep-inline-subtree.html"
     },
     {
-        "duration": "37.0",
+        "duration": "38.0",
         "name": "blink_perf.accessibility/many-text-changes-small-wait-between.html"
     },
     {
@@ -52,15 +52,15 @@
         "name": "blink_perf.bindings/first-child.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/gc-forest.html"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "blink_perf.bindings/gc-mini-tree.html"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "blink_perf.bindings/gc-tree.html"
     },
     {
@@ -100,7 +100,7 @@
         "name": "blink_perf.bindings/named-property-enumerator.html"
     },
     {
-        "duration": "19.0",
+        "duration": "20.0",
         "name": "blink_perf.bindings/node-list-access.html"
     },
     {
@@ -148,7 +148,7 @@
         "name": "blink_perf.bindings/structured-clone-json-deserialize.html"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "blink_perf.bindings/structured-clone-json-serialize.html"
     },
     {
@@ -196,11 +196,11 @@
         "name": "blink_perf.bindings/worker-structured-clone-json-from-worker.html"
     },
     {
-        "duration": "29.0",
+        "duration": "28.0",
         "name": "blink_perf.bindings/worker-structured-clone-json-roundtrip.html"
     },
     {
-        "duration": "29.0",
+        "duration": "28.0",
         "name": "blink_perf.bindings/worker-structured-clone-json-to-worker.html"
     },
     {
@@ -236,7 +236,7 @@
         "name": "blink_perf.bindings/worker-transferable-to-worker.html"
     },
     {
-        "duration": "23.0",
+        "duration": "25.0",
         "name": "blink_perf.css/AttributeDescendantSelector.html"
     },
     {
@@ -244,7 +244,7 @@
         "name": "blink_perf.css/CSSLogicalDirection.html"
     },
     {
-        "duration": "14.0",
+        "duration": "15.0",
         "name": "blink_perf.css/CSSPropertySetterGetter.html"
     },
     {
@@ -256,11 +256,11 @@
         "name": "blink_perf.css/CSSPropertyUpdateValue.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/ChangeStyleCSSVariableRecalc.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.css/ChangeStyleChildClassSelector.html"
     },
     {
@@ -272,15 +272,15 @@
         "name": "blink_perf.css/ChangeStyleCustomPropertyDeclaration.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/ChangeStyleElementSelector.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.css/ChangeStyleGrandChildElementSelector.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.css/ChangeStyleMultipleClassSelector.html"
     },
     {
@@ -296,15 +296,15 @@
         "name": "blink_perf.css/ChangeStylePairOfNthChildSelector.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/ChangeStylePartialAttributeMatchingSelector.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/ChangeStyleQualifiedDataAttributeSelector.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.css/ChangeStyleQualifiedDataAttributeWithValueSelector.html"
     },
     {
@@ -328,11 +328,11 @@
         "name": "blink_perf.css/ChangeStyleUniversalSelector.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/ChangeStyleUnqualifiedDataAttributeSelector.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.css/ChangeStyleUnqualifiedDataAttributeWithValueSelector.html"
     },
     {
@@ -340,7 +340,7 @@
         "name": "blink_perf.css/ClassDescendantSelector.html"
     },
     {
-        "duration": "15.0",
+        "duration": "16.0",
         "name": "blink_perf.css/ClassInvalidation.html"
     },
     {
@@ -356,7 +356,7 @@
         "name": "blink_perf.css/CustomPropertiesNonRootInheritance.html"
     },
     {
-        "duration": "10.0",
+        "duration": "8.0",
         "name": "blink_perf.css/CustomPropertiesPendingSubstitution.html"
     },
     {
@@ -396,11 +396,11 @@
         "name": "blink_perf.css/SelectorCountScaling.html"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "blink_perf.dom/custom-element-default-style-with-shadow.html"
     },
     {
-        "duration": "9.0",
+        "duration": "10.0",
         "name": "blink_perf.dom/custom-element-default-style.html"
     },
     {
@@ -408,7 +408,7 @@
         "name": "blink_perf.dom/long-sibling-list.html"
     },
     {
-        "duration": "7.0",
+        "duration": "8.0",
         "name": "blink_perf.dom/modify-element-classname.html"
     },
     {
@@ -416,307 +416,307 @@
         "name": "blink_perf.dom/modify-element-id.html"
     },
     {
-        "duration": "5.0",
+        "duration": "6.0",
         "name": "blink_perf.dom/modify-element-title.html"
     },
     {
-        "duration": "10.0",
+        "duration": "11.0",
         "name": "blink_perf.dom/select-multiple-add.html"
     },
     {
-        "duration": "10.0",
+        "duration": "11.0",
         "name": "blink_perf.dom/select-single-add.html"
     },
     {
-        "duration": "9.0",
+        "duration": "11.0",
         "name": "blink_perf.dom/select-single-remove.html"
     },
     {
-        "duration": "17.0",
+        "duration": "20.0",
         "name": "blink_perf.events/EventsDispatching.html"
     },
     {
-        "duration": "12.0",
+        "duration": "16.0",
         "name": "blink_perf.events/EventsDispatchingInDeeplyNestedV1ShadowTrees.html"
     },
     {
-        "duration": "26.0",
+        "duration": "31.0",
         "name": "blink_perf.events/EventsDispatchingInV1ShadowTrees.html"
     },
     {
-        "duration": "25.0",
+        "duration": "30.0",
         "name": "blink_perf.events/hit-test-lots-of-layers.html"
     },
     {
-        "duration": "11.0",
+        "duration": "14.0",
         "name": "blink_perf.events/is-input-pending-default-events.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.events/is-input-pending-include-continuous-events.html"
     },
     {
-        "duration": "28.0",
+        "duration": "33.0",
         "name": "blink_perf.image_decoder/decode-gif.html"
     },
     {
-        "duration": "20.0",
+        "duration": "26.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h1v1.html"
     },
     {
-        "duration": "20.0",
+        "duration": "26.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h1v2.html"
     },
     {
-        "duration": "21.0",
+        "duration": "27.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h2v1.html"
     },
     {
-        "duration": "21.0",
+        "duration": "27.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h2v2.html"
     },
     {
-        "duration": "31.0",
+        "duration": "41.0",
         "name": "blink_perf.image_decoder/decode-lossless-webp.html"
     },
     {
-        "duration": "18.0",
+        "duration": "23.0",
         "name": "blink_perf.image_decoder/decode-lossy-webp.html"
     },
     {
-        "duration": "25.0",
+        "duration": "31.0",
         "name": "blink_perf.image_decoder/decode-png-palette-opaque.html"
     },
     {
-        "duration": "20.0",
+        "duration": "28.0",
         "name": "blink_perf.image_decoder/decode-png-palette.html"
     },
     {
-        "duration": "32.0",
+        "duration": "42.0",
         "name": "blink_perf.image_decoder/decode-png.html"
     },
     {
-        "duration": "23.0",
+        "duration": "25.0",
         "name": "blink_perf.layout/ArabicLineLayout.html"
     },
     {
-        "duration": "4.0",
+        "duration": "7.0",
         "name": "blink_perf.layout/Shapes/MultipleShapes.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/SimpleTextPathLineLayout.html"
     },
     {
-        "duration": "23.0",
+        "duration": "30.0",
         "name": "blink_perf.layout/abspos.html"
     },
     {
-        "duration": "9.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/add-remove-inline-floats.html"
     },
     {
-        "duration": "5.0",
+        "duration": "8.0",
         "name": "blink_perf.layout/animate-abspos-deep.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/attach-inlines-2.html"
     },
     {
-        "duration": "9.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/attach-inlines.html"
     },
     {
-        "duration": "10.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/auto-grid-lots-of-data.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/auto-grid-lots-of-spanning-data.html"
     },
     {
-        "duration": "13.0",
+        "duration": "19.0",
         "name": "blink_perf.layout/change-text-css-contain.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/chapter-reflow-once-random.html"
     },
     {
-        "duration": "11.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/chapter-reflow-once.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/chapter-reflow-thrice.html"
     },
     {
-        "duration": "10.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/chapter-reflow-twice.html"
     },
     {
-        "duration": "10.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/chapter-reflow.html"
     },
     {
-        "duration": "6.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/character_fallback.html"
     },
     {
-        "duration": "9.0",
+        "duration": "12.0",
         "name": "blink_perf.layout/contain-content-style-change.html"
     },
     {
-        "duration": "5.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/culled-inline-bounding-rects.html"
     },
     {
-        "duration": "4.0",
+        "duration": "7.0",
         "name": "blink_perf.layout/culled-inline-hittest.html"
     },
     {
-        "duration": "10.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/deeply-nested-grid.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/editing_append.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/editing_append_single_line.html"
     },
     {
-        "duration": "9.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/editing_delete.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/editing_insert.html"
     },
     {
-        "duration": "9.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/editing_prepend.html"
     },
     {
-        "duration": "19.0",
+        "duration": "24.0",
         "name": "blink_perf.layout/fit-content-change-available-size-blocks.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/fit-content-change-available-size-text.html"
     },
     {
-        "duration": "11.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/fixed-grid-lots-of-data.html"
     },
     {
-        "duration": "11.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/fixed-grid-lots-of-stretched-data.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/flexbox-column-nowrap.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/flexbox-column-wrap.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/flexbox-deeply-nested-column-flow.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/flexbox-hittest.html"
     },
     {
-        "duration": "11.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/flexbox-input.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/flexbox-lots-of-data.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/flexbox-row-nowrap.html"
     },
     {
-        "duration": "14.0",
+        "duration": "20.0",
         "name": "blink_perf.layout/flexbox-row-stretch-height-definite.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/flexbox-row-stretch-height-indefinite.html"
     },
     {
-        "duration": "10.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/flexbox-row-wrap.html"
     },
     {
-        "duration": "9.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/flexbox-with-stretch-layout.html"
     },
     {
-        "duration": "15.0",
+        "duration": "20.0",
         "name": "blink_perf.layout/flexbox_with_list_item.html"
     },
     {
-        "duration": "14.0",
+        "duration": "18.0",
         "name": "blink_perf.layout/floats_100_100.html"
     },
     {
-        "duration": "14.0",
+        "duration": "19.0",
         "name": "blink_perf.layout/floats_100_100_nested.html"
     },
     {
-        "duration": "15.0",
+        "duration": "19.0",
         "name": "blink_perf.layout/floats_10_1000.html"
     },
     {
-        "duration": "7.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/floats_20_100.html"
     },
     {
-        "duration": "14.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/floats_20_100_nested.html"
     },
     {
-        "duration": "6.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/floats_2_100.html"
     },
     {
-        "duration": "5.0",
+        "duration": "7.0",
         "name": "blink_perf.layout/floats_2_100_nested.html"
     },
     {
-        "duration": "7.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/floats_50_100.html"
     },
     {
-        "duration": "7.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/floats_50_100_nested.html"
     },
     {
-        "duration": "10.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/floats_show_hide.html"
     },
     {
-        "duration": "32.0",
+        "duration": "25.0",
         "name": "blink_perf.layout/hindi-line-layout.html"
     },
     {
-        "duration": "7.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/hittest-block-children.html"
     },
     {
-        "duration": "10.0",
+        "duration": "12.0",
         "name": "blink_perf.layout/japanese-kokoro-insert.html"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "blink_perf.layout/large-grid.html"
     },
     {
@@ -724,11 +724,11 @@
         "name": "blink_perf.layout/large-spanning-grid-item.html"
     },
     {
-        "duration": "29.0",
+        "duration": "27.0",
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans-wider-than-table.html"
     },
     {
-        "duration": "28.0",
+        "duration": "25.0",
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans.html"
     },
     {
@@ -736,7 +736,7 @@
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-no-colspans.html"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "blink_perf.layout/latin-ebook-resize.html"
     },
     {
@@ -744,11 +744,11 @@
         "name": "blink_perf.layout/latin-ebook.html"
     },
     {
-        "duration": "10.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/layers_overlap_2d.html"
     },
     {
-        "duration": "10.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/layers_overlap_3d.html"
     },
     {
@@ -756,11 +756,11 @@
         "name": "blink_perf.layout/line-layout-fit-content-break-word.html"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "blink_perf.layout/line-layout-fit-content.html"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "blink_perf.layout/line-layout-line-height.html"
     },
     {
@@ -772,7 +772,7 @@
         "name": "blink_perf.layout/line-layout-repeat-append.html"
     },
     {
-        "duration": "14.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/line-layout.html"
     },
     {
@@ -784,7 +784,7 @@
         "name": "blink_perf.layout/long-line-nowrap-spans-collapse.html"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/long-line-nowrap.html"
     },
     {
@@ -792,11 +792,11 @@
         "name": "blink_perf.layout/many-block-children-auto-inline-size.html"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/many-block-children-fixed-inline-size.html"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/multicol/balance-forced-breaks.html"
     },
     {
@@ -808,271 +808,271 @@
         "name": "blink_perf.layout/multicol/fixed-height-with-spanner-and-nested-tables.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/multicol/lots-of-small-nested-unbreakable-blocks-autofill.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/multicol/lots-of-small-unbreakable-blocks-autofill.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/multicol/lots-of-text-autofill.html"
     },
     {
-        "duration": "14.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/multicol/lots-of-text-balanced-orphans-widows.html"
     },
     {
-        "duration": "13.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/multicol/lots-of-text-balanced.html"
     },
     {
-        "duration": "46.0",
+        "duration": "38.0",
         "name": "blink_perf.layout/multicol/nested-forced-breaks.html"
     },
     {
-        "duration": "11.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/multicol/tall-content-short-columns-realistic.html"
     },
     {
-        "duration": "14.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/multicol/tall-content-short-columns.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/nested-blocks-with-percent-height-and-max-height.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/nested-grid-lots-of-tracks.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/nested-grid.html"
     },
     {
-        "duration": "13.0",
+        "duration": "9.0",
         "name": "blink_perf.layout/nested-percent-height-tables.html"
     },
     {
-        "duration": "21.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/ruby.html"
     },
     {
-        "duration": "91.0",
+        "duration": "76.0",
         "name": "blink_perf.layout/subtree-detaching.html"
     },
     {
-        "duration": "13.0",
+        "duration": "8.0",
         "name": "blink_perf.layout/vertical-japanese-kokoro-insert.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.layout/word-break-break-all.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.layout/word-break-break-word.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.layout/word-wrap-break-word.html"
     },
     {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "blink_perf.owp_storage/blob-perf-files.html"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "blink_perf.owp_storage/blob-perf-ipc.html"
     },
     {
-        "duration": "25.0",
+        "duration": "20.0",
         "name": "blink_perf.owp_storage/blob-perf-shm.html"
     },
     {
-        "duration": "29.0",
+        "duration": "22.0",
         "name": "blink_perf.owp_storage/blob-perf-tiny.html"
     },
     {
-        "duration": "28.0",
+        "duration": "22.0",
         "name": "blink_perf.owp_storage/idb-load-docs.html"
     },
     {
-        "duration": "28.0",
+        "duration": "20.0",
         "name": "blink_perf.paint/appending-text.html"
     },
     {
-        "duration": "40.0",
+        "duration": "26.0",
         "name": "blink_perf.paint/color-changes.html"
     },
     {
-        "duration": "36.0",
+        "duration": "22.0",
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "34.0",
+        "duration": "21.0",
         "name": "blink_perf.paint/complex-iframe-filtered.html"
     },
     {
-        "duration": "72.0",
+        "duration": "50.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
     {
-        "duration": "35.0",
+        "duration": "23.0",
         "name": "blink_perf.paint/containment-resize.html"
     },
     {
-        "duration": "33.0",
+        "duration": "20.0",
         "name": "blink_perf.paint/fixed-and-many-layers-scroll.html"
     },
     {
-        "duration": "37.0",
+        "duration": "23.0",
         "name": "blink_perf.paint/large-table-background-change.html"
     },
     {
-        "duration": "32.0",
+        "duration": "20.0",
         "name": "blink_perf.paint/large-table-collapsed-border-change.html"
     },
     {
-        "duration": "28.0",
+        "duration": "18.0",
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "29.0",
+        "duration": "18.0",
         "name": "blink_perf.paint/move-text-with-mask.html"
     },
     {
-        "duration": "33.0",
+        "duration": "20.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
     {
-        "duration": "45.0",
+        "duration": "27.0",
         "name": "blink_perf.paint/transform-changes.html"
     },
     {
-        "duration": "17.0",
+        "duration": "15.0",
         "name": "blink_perf.parser/css-parser-yui.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.parser/declarative-shadow-dom-cloning.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/declarative-shadow-dom.html"
     },
     {
-        "duration": "15.0",
+        "duration": "9.0",
         "name": "blink_perf.parser/html-parser-threaded.html"
     },
     {
-        "duration": "16.0",
+        "duration": "9.0",
         "name": "blink_perf.parser/html-parser.html"
     },
     {
-        "duration": "55.0",
+        "duration": "43.0",
         "name": "blink_perf.parser/html5-full-render.html"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "blink_perf.parser/iframe-append-remove.html"
     },
     {
-        "duration": "16.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/innerHTML-setter-siblings.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/innerHTML-setter.html"
     },
     {
-        "duration": "14.0",
+        "duration": "9.0",
         "name": "blink_perf.parser/query-selector-all-attribute-complex.html"
     },
     {
-        "duration": "14.0",
+        "duration": "9.0",
         "name": "blink_perf.parser/query-selector-all-attribute.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-class-deep.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-class-first.html"
     },
     {
-        "duration": "16.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-class-last.html"
     },
     {
-        "duration": "15.0",
+        "duration": "9.0",
         "name": "blink_perf.parser/query-selector-all-class.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-deep.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-first.html"
     },
     {
-        "duration": "16.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-all-id-deep.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-id-first.html"
     },
     {
-        "duration": "17.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-all-id-last.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-last.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-deep.html"
     },
     {
-        "duration": "14.0",
+        "duration": "15.0",
         "name": "blink_perf.parser/query-selector-first.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-id-deep.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-id-last.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-last.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/simple-url.html"
     },
     {
-        "duration": "16.0",
+        "duration": "12.0",
         "name": "blink_perf.parser/textarea-parsing.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/tiny-innerHTML.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/url-parser.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/xml-parser.html"
     },
     {
@@ -1084,7 +1084,7 @@
         "name": "blink_perf.shadow_dom/imperative-api-appendchild.html"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/imperative-api-assign.html"
     },
     {
@@ -1096,19 +1096,19 @@
         "name": "blink_perf.shadow_dom/imperative-api-assigned-slot.html"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/imperative-api-custom-detail-summary-large.html"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/imperative-api-custom-detail-summary.html"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/imperative-api-detail-summary-large.html"
     },
     {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/imperative-api-detail-summary.html"
     },
     {
@@ -1116,79 +1116,79 @@
         "name": "blink_perf.shadow_dom/imperative-api-insertbefore.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/imperative-api.html"
     },
     {
-        "duration": "30.0",
+        "duration": "21.0",
         "name": "blink_perf.shadow_dom/shadow-dom-overhead.html"
     },
     {
-        "duration": "9.0",
+        "duration": "5.0",
         "name": "blink_perf.shadow_dom/shadow-style-share-attr-selectors.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/shadow-style-share-media-query.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/shadow-style-share-with-distribution.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/shadow-style-share.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/style-sheet-insert.html"
     },
     {
-        "duration": "17.0",
+        "duration": "12.0",
         "name": "blink_perf.shadow_dom/v1-distribution-disconnected-and-reconnected.html"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/v1-distribution.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/v1-host-child-append.html"
     },
     {
-        "duration": "19.0",
+        "duration": "11.0",
         "name": "blink_perf.shadow_dom/v1-large-deep-distribution.html"
     },
     {
-        "duration": "23.0",
+        "duration": "16.0",
         "name": "blink_perf.shadow_dom/v1-large-deep-layout.html"
     },
     {
-        "duration": "7.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-large-shallow-append-layout.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-large-shallow-distribution.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-large-shallow-layout.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-large-shallow-prepend-layout.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/v1-mutate-deep-tree-then-re-layout.html"
     },
     {
-        "duration": "6.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-mutate-deep-tree-then-slot-assigned-nodes.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-mutate-deep-tree-then-slot-flatten.html"
     },
     {
@@ -1196,47 +1196,47 @@
         "name": "blink_perf.shadow_dom/v1-mutate-shallow-tree-then-re-layout.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-mutate-shallow-tree-then-slot-assigned-nodes.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-mutate-shallow-tree-then-slot-flatten.html"
     },
     {
-        "duration": "7.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-slot-append.html"
     },
     {
-        "duration": "11.0",
+        "duration": "5.0",
         "name": "blink_perf.shadow_dom/v1-small-deep-distribution.html"
     },
     {
-        "duration": "8.0",
+        "duration": "5.0",
         "name": "blink_perf.shadow_dom/v1-small-deep-layout.html"
     },
     {
-        "duration": "5.0",
+        "duration": "3.0",
         "name": "blink_perf.shadow_dom/v1-small-shallow-distribution.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.shadow_dom/v1-small-shallow-layout.html"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "blink_perf.svg/AzLizardBenjiPark.html"
     },
     {
-        "duration": "10.0",
+        "duration": "7.0",
         "name": "blink_perf.svg/Bamboo.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/Cactus.html"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/Cowboy.html"
     },
     {
@@ -1244,51 +1244,51 @@
         "name": "blink_perf.svg/Cowboy_transform.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/CrawFishGanson.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/Debian.html"
     },
     {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/DropsOnABlade.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/FlowerFromMyGarden.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/FoodLeifLodahl.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/France.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/FrancoBolloGnomeEzechi.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/GearFlowers.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/HarveyRayner.html"
     },
     {
-        "duration": "7.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/HereGear.html"
     },
     {
-        "duration": "15.0",
+        "duration": "8.0",
         "name": "blink_perf.svg/MtSaintHelens.html"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/Samurai.html"
     },
     {
@@ -1296,123 +1296,123 @@
         "name": "blink_perf.svg/SierpinskiCarpet.html"
     },
     {
-        "duration": "9.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/SvgCubics.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/SvgHitTesting.html"
     },
     {
-        "duration": "16.0",
+        "duration": "11.0",
         "name": "blink_perf.svg/SvgNestedUse.html"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "blink_perf.svg/UnderTheSee.html"
     },
     {
-        "duration": "7.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/WorldIso.html"
     },
     {
-        "duration": "12.0",
+        "duration": "7.0",
         "name": "blink_perf.svg/Worldcup.html"
     },
     {
-        "duration": "38.0",
+        "duration": "33.0",
         "name": "blink_perf.webaudio/audio-buffer-source-node.html"
     },
     {
-        "duration": "149.0",
+        "duration": "182.0",
         "name": "blink_perf.webaudio/audio-worklet-node.html"
     },
     {
-        "duration": "143.0",
+        "duration": "180.0",
         "name": "blink_perf.webaudio/biquad-filter-node.html"
     },
     {
-        "duration": "144.0",
+        "duration": "180.0",
         "name": "blink_perf.webaudio/gain-node.html"
     },
     {
-        "duration": "59.0",
+        "duration": "72.0",
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "13.0",
+        "duration": "10.0",
         "name": "blink_perf.webgl/binding-draw-arrays.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "blink_perf.webgpu/binding-draw.html"
     },
     {
-        "duration": "16.0",
+        "duration": "11.0",
         "name": "blink_perf.webgpu_fast_call/binding-draw.html"
     },
     {
-        "duration": "39.0",
+        "duration": "47.0",
         "name": "desktop_ui/tab_search:clean_slate"
     },
     {
-        "duration": "76.0",
+        "duration": "92.0",
         "name": "desktop_ui/tab_search:close_and_open:2020"
     },
     {
-        "duration": "61.0",
+        "duration": "74.0",
         "name": "desktop_ui/tab_search:close_and_open:loading:2020"
     },
     {
-        "duration": "42.0",
+        "duration": "51.0",
         "name": "desktop_ui/tab_search:measure_memory:2tab_search"
     },
     {
-        "duration": "42.0",
+        "duration": "41.0",
         "name": "desktop_ui/tab_search:measure_memory:3tab_search"
     },
     {
-        "duration": "43.0",
+        "duration": "51.0",
         "name": "desktop_ui/tab_search:measure_memory:after"
     },
     {
-        "duration": "44.0",
+        "duration": "52.0",
         "name": "desktop_ui/tab_search:measure_memory:before"
     },
     {
-        "duration": "47.0",
+        "duration": "55.0",
         "name": "desktop_ui/tab_search:measure_memory:multiwindow"
     },
     {
-        "duration": "153.0",
+        "duration": "162.0",
         "name": "desktop_ui/tab_search:scroll_up_and_down:2020"
     },
     {
-        "duration": "352.0",
+        "duration": "423.0",
         "name": "desktop_ui/tab_search:top100:2020"
     },
     {
-        "duration": "155.0",
+        "duration": "168.0",
         "name": "desktop_ui/tab_search:top100:loading:2020"
     },
     {
-        "duration": "82.0",
+        "duration": "93.0",
         "name": "desktop_ui/tab_search:top10:2020"
     },
     {
-        "duration": "55.0",
+        "duration": "64.0",
         "name": "desktop_ui/tab_search:top10:loading:2020"
     },
     {
-        "duration": "206.0",
+        "duration": "258.0",
         "name": "desktop_ui/tab_search:top50:2020"
     },
     {
-        "duration": "95.0",
+        "duration": "104.0",
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
@@ -1420,7 +1420,7 @@
         "name": "dromaeo/http://dromaeo.com?dom-attr"
     },
     {
-        "duration": "34.0",
+        "duration": "35.0",
         "name": "dromaeo/http://dromaeo.com?dom-modify"
     },
     {
@@ -1436,75 +1436,75 @@
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
     {
-        "duration": "4.0",
+        "duration": "5.0",
         "name": "dummy_benchmark.stable_benchmark_1/dummy_page.html"
     },
     {
-        "duration": "230.0",
+        "duration": "256.0",
         "name": "jetstream/JetStream"
     },
     {
-        "duration": "319.0",
+        "duration": "386.0",
         "name": "jetstream2/JetStream2"
     },
     {
-        "duration": "39.0",
+        "duration": "44.0",
         "name": "kraken/http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html"
     },
     {
-        "duration": "60.0",
+        "duration": "105.0",
         "name": "loading.desktop/24h_cold"
     },
     {
-        "duration": "63.0",
+        "duration": "91.0",
         "name": "loading.desktop/24h_warm"
     },
     {
-        "duration": "55.0",
+        "duration": "66.0",
         "name": "loading.desktop/AirBnB_cold"
     },
     {
-        "duration": "66.0",
+        "duration": "76.0",
         "name": "loading.desktop/AirBnB_warm"
     },
     {
-        "duration": "38.0",
+        "duration": "44.0",
         "name": "loading.desktop/Aljayyash_cold"
     },
     {
-        "duration": "38.0",
+        "duration": "44.0",
         "name": "loading.desktop/Aljayyash_warm"
     },
     {
-        "duration": "36.0",
+        "duration": "32.0",
         "name": "loading.desktop/AllRecipes_cold"
     },
     {
-        "duration": "66.0",
+        "duration": "61.0",
         "name": "loading.desktop/AllRecipes_warm"
     },
     {
-        "duration": "53.0",
+        "duration": "49.0",
         "name": "loading.desktop/ArsTechnica_cold"
     },
     {
-        "duration": "62.0",
+        "duration": "59.0",
         "name": "loading.desktop/ArsTechnica_warm"
     },
     {
-        "duration": "77.0",
+        "duration": "48.0",
         "name": "loading.desktop/Baidu_cold"
     },
     {
-        "duration": "59.0",
+        "duration": "46.0",
         "name": "loading.desktop/Baidu_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "45.0",
         "name": "loading.desktop/Bhaskar_cold"
     },
     {
-        "duration": "58.0",
+        "duration": "79.0",
         "name": "loading.desktop/Bhaskar_warm"
     },
     {
@@ -1512,11 +1512,11 @@
         "name": "loading.desktop/Chosun_cold"
     },
     {
-        "duration": "54.0",
+        "duration": "53.0",
         "name": "loading.desktop/Chosun_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "48.0",
         "name": "loading.desktop/Colorado.edu_cold"
     },
     {
@@ -1528,47 +1528,47 @@
         "name": "loading.desktop/Danawa_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "loading.desktop/Danawa_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "66.0",
         "name": "loading.desktop/Daum_cold"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "loading.desktop/Daum_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "loading.desktop/Donga_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "loading.desktop/Donga_warm"
     },
     {
-        "duration": "54.0",
+        "duration": "50.0",
         "name": "loading.desktop/Economist_cold"
     },
     {
-        "duration": "64.0",
+        "duration": "60.0",
         "name": "loading.desktop/Economist_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "50.0",
         "name": "loading.desktop/Elmundo_cold"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "loading.desktop/Elmundo_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "50.0",
         "name": "loading.desktop/FC2Blog_cold"
     },
     {
-        "duration": "50.0",
+        "duration": "53.0",
         "name": "loading.desktop/FC2Blog_warm"
     },
     {
@@ -1576,19 +1576,19 @@
         "name": "loading.desktop/FIFA_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "68.0",
         "name": "loading.desktop/FIFA_warm"
     },
     {
-        "duration": "57.0",
+        "duration": "62.0",
         "name": "loading.desktop/FarsNews_cold"
     },
     {
-        "duration": "41.0",
+        "duration": "46.0",
         "name": "loading.desktop/FarsNews_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "66.0",
         "name": "loading.desktop/Flickr_cold"
     },
     {
@@ -1596,135 +1596,135 @@
         "name": "loading.desktop/Flickr_warm"
     },
     {
-        "duration": "46.0",
+        "duration": "49.0",
         "name": "loading.desktop/FlipKart_cold"
     },
     {
-        "duration": "48.0",
+        "duration": "51.0",
         "name": "loading.desktop/FlipKart_warm"
     },
     {
-        "duration": "38.0",
+        "duration": "57.0",
         "name": "loading.desktop/Free.fr_cold"
     },
     {
-        "duration": "38.0",
+        "duration": "40.0",
         "name": "loading.desktop/Free.fr_warm"
     },
     {
-        "duration": "66.0",
+        "duration": "46.0",
         "name": "loading.desktop/HTML5Rocks_cold"
     },
     {
-        "duration": "48.0",
+        "duration": "44.0",
         "name": "loading.desktop/HTML5Rocks_warm"
     },
     {
-        "duration": "38.0",
+        "duration": "43.0",
         "name": "loading.desktop/Haraj_cold"
     },
     {
-        "duration": "38.0",
+        "duration": "43.0",
         "name": "loading.desktop/Haraj_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "50.0",
         "name": "loading.desktop/HatenaBookmark_cold"
     },
     {
-        "duration": "51.0",
+        "duration": "54.0",
         "name": "loading.desktop/HatenaBookmark_warm"
     },
     {
-        "duration": "50.0",
+        "duration": "60.0",
         "name": "loading.desktop/IGN_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "64.0",
         "name": "loading.desktop/IGN_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "47.0",
         "name": "loading.desktop/IMDB_cold"
     },
     {
-        "duration": "54.0",
+        "duration": "50.0",
         "name": "loading.desktop/IMDB_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "41.0",
         "name": "loading.desktop/IndiaTimes_cold"
     },
     {
-        "duration": "51.0",
+        "duration": "45.0",
         "name": "loading.desktop/IndiaTimes_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "51.0",
         "name": "loading.desktop/Kakaku_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "56.0",
         "name": "loading.desktop/Kakaku_warm"
     },
     {
-        "duration": "68.0",
+        "duration": "99.0",
         "name": "loading.desktop/Kenh14_cold"
     },
     {
-        "duration": "80.0",
+        "duration": "112.0",
         "name": "loading.desktop/Kenh14_warm"
     },
     {
-        "duration": "39.0",
+        "duration": "41.0",
         "name": "loading.desktop/Mercadolivre_cold"
     },
     {
-        "duration": "68.0",
+        "duration": "45.0",
         "name": "loading.desktop/Mercadolivre_warm"
     },
     {
-        "duration": "59.0",
+        "duration": "88.0",
         "name": "loading.desktop/Naver_cold"
     },
     {
-        "duration": "70.0",
+        "duration": "89.0",
         "name": "loading.desktop/Naver_warm"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "loading.desktop/Orange_cold"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "loading.desktop/Orange_warm"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "loading.desktop/Pantip_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "loading.desktop/Pantip_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "loading.desktop/PremierLeague_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "56.0",
         "name": "loading.desktop/PremierLeague_warm"
     },
     {
-        "duration": "62.0",
+        "duration": "50.0",
         "name": "loading.desktop/QQ_cold"
     },
     {
-        "duration": "69.0",
+        "duration": "56.0",
         "name": "loading.desktop/QQ_warm"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "loading.desktop/REI_cold"
     },
     {
@@ -1732,35 +1732,35 @@
         "name": "loading.desktop/REI_warm"
     },
     {
-        "duration": "59.0",
+        "duration": "47.0",
         "name": "loading.desktop/Ruten_cold"
     },
     {
-        "duration": "60.0",
+        "duration": "48.0",
         "name": "loading.desktop/Ruten_warm"
     },
     {
-        "duration": "61.0",
+        "duration": "48.0",
         "name": "loading.desktop/Sina_cold"
     },
     {
-        "duration": "66.0",
+        "duration": "53.0",
         "name": "loading.desktop/Sina_warm"
     },
     {
-        "duration": "65.0",
+        "duration": "53.0",
         "name": "loading.desktop/Taobao_cold"
     },
     {
-        "duration": "75.0",
+        "duration": "63.0",
         "name": "loading.desktop/Taobao_warm"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "loading.desktop/TheOnion_cold"
     },
     {
-        "duration": "36.0",
+        "duration": "32.0",
         "name": "loading.desktop/TheOnion_warm"
     },
     {
@@ -1768,11 +1768,11 @@
         "name": "loading.desktop/TheVerge_cold"
     },
     {
-        "duration": "59.0",
+        "duration": "86.0",
         "name": "loading.desktop/TheVerge_warm"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "loading.desktop/TicketMaster_cold"
     },
     {
@@ -1780,55 +1780,55 @@
         "name": "loading.desktop/TicketMaster_warm"
     },
     {
-        "duration": "65.0",
+        "duration": "95.0",
         "name": "loading.desktop/Vietnamnet_cold"
     },
     {
-        "duration": "74.0",
+        "duration": "106.0",
         "name": "loading.desktop/Vietnamnet_warm"
     },
     {
-        "duration": "62.0",
+        "duration": "91.0",
         "name": "loading.desktop/Vnexpress_cold"
     },
     {
-        "duration": "70.0",
+        "duration": "99.0",
         "name": "loading.desktop/Vnexpress_warm"
     },
     {
-        "duration": "35.0",
+        "duration": "34.0",
         "name": "loading.desktop/Walgreens_cold"
     },
     {
-        "duration": "35.0",
+        "duration": "34.0",
         "name": "loading.desktop/Walgreens_warm"
     },
     {
-        "duration": "46.0",
+        "duration": "41.0",
         "name": "loading.desktop/Yandex_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "43.0",
         "name": "loading.desktop/Yandex_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "51.0",
         "name": "loading.desktop/amazon.co.jp_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "58.0",
         "name": "loading.desktop/amazon.co.jp_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "51.0",
         "name": "loading.desktop/ja.wikipedia_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "71.0",
         "name": "loading.desktop/ja.wikipedia_warm"
     },
     {
-        "duration": "54.0",
+        "duration": "53.0",
         "name": "loading.desktop/money.cnn_cold"
     },
     {
@@ -1836,55 +1836,55 @@
         "name": "loading.desktop/money.cnn_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "42.0",
         "name": "loading.desktop/ru.wikipedia_cold"
     },
     {
-        "duration": "51.0",
+        "duration": "45.0",
         "name": "loading.desktop/ru.wikipedia_warm"
     },
     {
-        "duration": "42.0",
+        "duration": "44.0",
         "name": "loading.desktop/uol.com.br_cold"
     },
     {
-        "duration": "51.0",
+        "duration": "53.0",
         "name": "loading.desktop/uol.com.br_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "49.0",
         "name": "loading.desktop/yahoo.co.jp_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "52.0",
         "name": "loading.desktop/yahoo.co.jp_warm"
     },
     {
-        "duration": "29.0",
+        "duration": "26.0",
         "name": "media.desktop/mse.html?media=aac_audio.mp4"
     },
     {
-        "duration": "29.0",
+        "duration": "26.0",
         "name": "media.desktop/mse.html?media=aac_audio.mp4,h264_video.mp4"
     },
     {
-        "duration": "28.0",
+        "duration": "25.0",
         "name": "media.desktop/mse.html?media=h264_video.mp4"
     },
     {
-        "duration": "28.0",
+        "duration": "24.0",
         "name": "media.desktop/mse.html?media=tulip0.av1.mp4"
     },
     {
-        "duration": "29.0",
+        "duration": "25.0",
         "name": "media.desktop/mse.html?media=tulip2.vp9.webm"
     },
     {
-        "duration": "148.0",
+        "duration": "155.0",
         "name": "media.desktop/video.html?src=boat_1080p60fps_vp9.webm"
     },
     {
-        "duration": "44.0",
+        "duration": "43.0",
         "name": "media.desktop/video.html?src=crowd1080.mp4"
     },
     {
@@ -1892,71 +1892,71 @@
         "name": "media.desktop/video.html?src=crowd1080.webm"
     },
     {
-        "duration": "34.0",
+        "duration": "42.0",
         "name": "media.desktop/video.html?src=crowd1080_vp9.webm"
     },
     {
-        "duration": "147.0",
+        "duration": "154.0",
         "name": "media.desktop/video.html?src=foodmarket_720p30fps.mp4"
     },
     {
-        "duration": "20.0",
+        "duration": "17.0",
         "name": "media.desktop/video.html?src=garden2_10s.mp4&seek"
     },
     {
-        "duration": "30.0",
+        "duration": "37.0",
         "name": "media.desktop/video.html?src=garden2_10s.webm&seek"
     },
     {
-        "duration": "20.0",
+        "duration": "17.0",
         "name": "media.desktop/video.html?src=smpte_3840x2160_60fps_vp9.webm&seek"
     },
     {
-        "duration": "34.0",
+        "duration": "31.0",
         "name": "media.desktop/video.html?src=tulip0.av1.mp4"
     },
     {
-        "duration": "28.0",
+        "duration": "24.0",
         "name": "media.desktop/video.html?src=tulip0.av1.mp4&seek"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.m4a&type=audio"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.mp3&type=audio"
     },
     {
-        "duration": "27.0",
+        "duration": "34.0",
         "name": "media.desktop/video.html?src=tulip2.mp3&type=audio&seek"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.mp4"
     },
     {
-        "duration": "41.0",
+        "duration": "49.0",
         "name": "media.desktop/video.html?src=tulip2.mp4&busyjs"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.ogg&type=audio"
     },
     {
-        "duration": "27.0",
+        "duration": "34.0",
         "name": "media.desktop/video.html?src=tulip2.ogg&type=audio&seek"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.vp9.webm"
     },
     {
-        "duration": "35.0",
+        "duration": "32.0",
         "name": "media.desktop/video.html?src=tulip2.vp9.webm&background"
     },
     {
-        "duration": "40.0",
+        "duration": "48.0",
         "name": "media.desktop/video.html?src=tulip2.vp9.webm_WiFi"
     },
     {
@@ -1992,95 +1992,95 @@
         "name": "memory.desktop/TrivialWebGLPageSharedPageState"
     },
     {
-        "duration": "70.0",
+        "duration": "65.0",
         "name": "memory.desktop/WebWorker"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "octane/Octane"
     },
     {
-        "duration": "58.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialAnimationPageSharedPageState"
     },
     {
-        "duration": "57.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialBlinkingCursorPageSharedPageState"
     },
     {
-        "duration": "58.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialBlurAnimationPageSharedPageState"
     },
     {
-        "duration": "58.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialCanvasPageSharedPageState"
     },
     {
-        "duration": "59.0",
+        "duration": "56.0",
         "name": "power.desktop/TrivialFullscreenVideoPageSharedPageState"
     },
     {
-        "duration": "57.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialGifPageSharedPageState"
     },
     {
-        "duration": "58.0",
+        "duration": "56.0",
         "name": "power.desktop/TrivialScrollingPageSharedPageState"
     },
     {
-        "duration": "58.0",
+        "duration": "55.0",
         "name": "power.desktop/TrivialWebGLPageSharedPageState"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "power.desktop/abcnews"
     },
     {
-        "duration": "60.0",
+        "duration": "57.0",
         "name": "power.desktop/indiatimes"
     },
     {
-        "duration": "58.0",
+        "duration": "55.0",
         "name": "power.desktop/instagram"
     },
     {
-        "duration": "59.0",
+        "duration": "56.0",
         "name": "power.desktop/microsoft"
     },
     {
-        "duration": "62.0",
+        "duration": "58.0",
         "name": "power.desktop/sina"
     },
     {
-        "duration": "68.0",
+        "duration": "64.0",
         "name": "power.desktop/slideshare"
     },
     {
-        "duration": "61.0",
+        "duration": "58.0",
         "name": "power.desktop/uol"
     },
     {
-        "duration": "32.0",
+        "duration": "29.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/amazon.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/blogger.html"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/booking.html"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/cnn.html"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/ebay.html"
     },
     {
-        "duration": "25.0",
+        "duration": "35.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/espn.html"
     },
     {
@@ -2144,7 +2144,7 @@
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahooanswers.html"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html"
     },
     {
@@ -2160,359 +2160,359 @@
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/youtube.html"
     },
     {
-        "duration": "57.0",
+        "duration": "65.0",
         "name": "rendering.desktop/accu_weather_2018"
     },
     {
-        "duration": "59.0",
+        "duration": "65.0",
         "name": "rendering.desktop/accu_weather_pinch_2018"
     },
     {
-        "duration": "48.0",
+        "duration": "53.0",
         "name": "rendering.desktop/amazon_2018"
     },
     {
-        "duration": "46.0",
+        "duration": "51.0",
         "name": "rendering.desktop/amazon_pinch_2018"
     },
     {
-        "duration": "48.0",
+        "duration": "53.0",
         "name": "rendering.desktop/analog_clock_svg"
     },
     {
-        "duration": "53.0",
+        "duration": "57.0",
         "name": "rendering.desktop/animometer_webgl"
     },
     {
-        "duration": "49.0",
+        "duration": "54.0",
         "name": "rendering.desktop/animometer_webgl_attrib_arrays"
     },
     {
-        "duration": "51.0",
+        "duration": "56.0",
         "name": "rendering.desktop/animometer_webgl_fast_call"
     },
     {
-        "duration": "51.0",
+        "duration": "56.0",
         "name": "rendering.desktop/animometer_webgl_indexed"
     },
     {
-        "duration": "50.0",
+        "duration": "56.0",
         "name": "rendering.desktop/animometer_webgl_indexed_fast_call"
     },
     {
-        "duration": "49.0",
+        "duration": "55.0",
         "name": "rendering.desktop/animometer_webgl_indexed_multi_draw"
     },
     {
-        "duration": "49.0",
+        "duration": "54.0",
         "name": "rendering.desktop/animometer_webgl_indexed_multi_draw_base_vertex_base_instance"
     },
     {
-        "duration": "49.0",
+        "duration": "54.0",
         "name": "rendering.desktop/animometer_webgl_multi_draw"
     },
     {
-        "duration": "54.0",
+        "duration": "59.0",
         "name": "rendering.desktop/aquarium"
     },
     {
-        "duration": "57.0",
+        "duration": "61.0",
         "name": "rendering.desktop/aquarium_20k"
     },
     {
-        "duration": "57.0",
+        "duration": "61.0",
         "name": "rendering.desktop/aquarium_20k_fast_call"
     },
     {
-        "duration": "49.0",
+        "duration": "54.0",
         "name": "rendering.desktop/background_color_animation"
     },
     {
-        "duration": "47.0",
+        "duration": "53.0",
         "name": "rendering.desktop/background_color_animation_with_gradient"
     },
     {
-        "duration": "48.0",
+        "duration": "53.0",
         "name": "rendering.desktop/balls_css_key_frame_animations"
     },
     {
-        "duration": "50.0",
+        "duration": "55.0",
         "name": "rendering.desktop/balls_css_key_frame_animations_composited_transform"
     },
     {
-        "duration": "47.0",
+        "duration": "44.0",
         "name": "rendering.desktop/balls_css_transition_2_properties"
     },
     {
-        "duration": "47.0",
+        "duration": "43.0",
         "name": "rendering.desktop/balls_css_transition_40_properties"
     },
     {
-        "duration": "47.0",
+        "duration": "43.0",
         "name": "rendering.desktop/balls_css_transition_all_properties"
     },
     {
-        "duration": "46.0",
+        "duration": "43.0",
         "name": "rendering.desktop/balls_javascript_canvas"
     },
     {
-        "duration": "46.0",
+        "duration": "43.0",
         "name": "rendering.desktop/balls_javascript_css"
     },
     {
-        "duration": "47.0",
+        "duration": "44.0",
         "name": "rendering.desktop/balls_svg_animations"
     },
     {
-        "duration": "56.0",
+        "duration": "47.0",
         "name": "rendering.desktop/blob"
     },
     {
-        "duration": "62.0",
+        "duration": "56.0",
         "name": "rendering.desktop/blogspot_2018"
     },
     {
-        "duration": "57.0",
+        "duration": "49.0",
         "name": "rendering.desktop/blogspot_pinch_2018"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/blur_rotating_background"
     },
     {
-        "duration": "47.0",
+        "duration": "40.0",
         "name": "rendering.desktop/booking.com_2018"
     },
     {
-        "duration": "49.0",
+        "duration": "41.0",
         "name": "rendering.desktop/booking_pinch_2018"
     },
     {
-        "duration": "45.0",
+        "duration": "42.0",
         "name": "rendering.desktop/bouncing_balls_15"
     },
     {
-        "duration": "49.0",
+        "duration": "45.0",
         "name": "rendering.desktop/bouncing_balls_shadow"
     },
     {
-        "duration": "50.0",
+        "duration": "44.0",
         "name": "rendering.desktop/bouncing_clipped_rectangles"
     },
     {
-        "duration": "50.0",
+        "duration": "43.0",
         "name": "rendering.desktop/bouncing_gradient_circles"
     },
     {
-        "duration": "48.0",
+        "duration": "42.0",
         "name": "rendering.desktop/bouncing_png_images"
     },
     {
-        "duration": "53.0",
+        "duration": "45.0",
         "name": "rendering.desktop/bouncing_svg_images"
     },
     {
-        "duration": "31.0",
+        "duration": "28.0",
         "name": "rendering.desktop/camera_to_webgl"
     },
     {
-        "duration": "49.0",
+        "duration": "42.0",
         "name": "rendering.desktop/canvas2d_to_texture.html"
     },
     {
-        "duration": "56.0",
+        "duration": "53.0",
         "name": "rendering.desktop/canvas_05000_pixels_per_second"
     },
     {
-        "duration": "54.0",
+        "duration": "50.0",
         "name": "rendering.desktop/canvas_10000_pixels_per_second"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "rendering.desktop/canvas_20000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "rendering.desktop/canvas_40000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "rendering.desktop/canvas_60000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "rendering.desktop/canvas_75000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "48.0",
         "name": "rendering.desktop/canvas_90000_pixels_per_second"
     },
     {
-        "duration": "53.0",
+        "duration": "43.0",
         "name": "rendering.desktop/canvas_animation_no_clear"
     },
     {
-        "duration": "54.0",
+        "duration": "43.0",
         "name": "rendering.desktop/canvas_arcs"
     },
     {
-        "duration": "52.0",
+        "duration": "43.0",
         "name": "rendering.desktop/canvas_font_cycler"
     },
     {
-        "duration": "49.0",
+        "duration": "43.0",
         "name": "rendering.desktop/canvas_lines"
     },
     {
-        "duration": "49.0",
+        "duration": "43.0",
         "name": "rendering.desktop/canvas_to_blob"
     },
     {
-        "duration": "48.0",
+        "duration": "41.0",
         "name": "rendering.desktop/canvas_to_canvas_draw"
     },
     {
-        "duration": "40.0",
+        "duration": "42.0",
         "name": "rendering.desktop/cats_unscaled"
     },
     {
-        "duration": "38.0",
+        "duration": "33.0",
         "name": "rendering.desktop/cats_viewport_width"
     },
     {
-        "duration": "51.0",
+        "duration": "45.0",
         "name": "rendering.desktop/cc_poster_circle"
     },
     {
-        "duration": "44.0",
+        "duration": "39.0",
         "name": "rendering.desktop/cc_scroll_text_only"
     },
     {
-        "duration": "57.0",
+        "duration": "45.0",
         "name": "rendering.desktop/chip_tune"
     },
     {
-        "duration": "53.0",
+        "duration": "44.0",
         "name": "rendering.desktop/cnn_2018"
     },
     {
-        "duration": "63.0",
+        "duration": "54.0",
         "name": "rendering.desktop/cnn_pinch_2018"
     },
     {
-        "duration": "56.0",
+        "duration": "41.0",
         "name": "rendering.desktop/compositor_heavy_animation"
     },
     {
-        "duration": "50.0",
+        "duration": "40.0",
         "name": "rendering.desktop/crafty_mind"
     },
     {
-        "duration": "59.0",
+        "duration": "42.0",
         "name": "rendering.desktop/css_animations_many_keyframes"
     },
     {
-        "duration": "56.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_animations_simultaneous_inline_style"
     },
     {
-        "duration": "51.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_animations_simultaneous_new_element"
     },
     {
-        "duration": "46.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_animations_simultaneous_style_element"
     },
     {
-        "duration": "45.0",
+        "duration": "39.0",
         "name": "rendering.desktop/css_animations_simultaneous_updating_class"
     },
     {
-        "duration": "46.0",
+        "duration": "39.0",
         "name": "rendering.desktop/css_animations_staggered_infinite_iterations"
     },
     {
-        "duration": "50.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_staggered_inline_style"
     },
     {
-        "duration": "51.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_staggered_new_element"
     },
     {
-        "duration": "51.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_staggered_style_element"
     },
     {
-        "duration": "51.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_staggered_updating_class"
     },
     {
-        "duration": "51.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_triggered_inline_style"
     },
     {
-        "duration": "51.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_triggered_new_element"
     },
     {
-        "duration": "51.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_animations_triggered_style_element"
     },
     {
-        "duration": "50.0",
+        "duration": "41.0",
         "name": "rendering.desktop/css_animations_triggered_updating_class"
     },
     {
-        "duration": "51.0",
+        "duration": "45.0",
         "name": "rendering.desktop/css_opacity_plus_n_layers_99"
     },
     {
-        "duration": "47.0",
+        "duration": "39.0",
         "name": "rendering.desktop/css_transitions_inline_style"
     },
     {
-        "duration": "47.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_transitions_new_element"
     },
     {
-        "duration": "47.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_transitions_staggered_inline_style"
     },
     {
-        "duration": "50.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_transitions_staggered_new_element"
     },
     {
-        "duration": "48.0",
+        "duration": "40.0",
         "name": "rendering.desktop/css_transitions_staggered_style_element"
     },
     {
-        "duration": "47.0",
+        "duration": "45.0",
         "name": "rendering.desktop/css_transitions_staggered_updating_class"
     },
     {
-        "duration": "47.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_transitions_style_element"
     },
     {
-        "duration": "49.0",
+        "duration": "44.0",
         "name": "rendering.desktop/css_transitions_triggered_inline_style"
     },
     {
-        "duration": "51.0",
+        "duration": "44.0",
         "name": "rendering.desktop/css_transitions_triggered_new_element"
     },
     {
-        "duration": "50.0",
+        "duration": "44.0",
         "name": "rendering.desktop/css_transitions_triggered_style_element"
     },
     {
-        "duration": "49.0",
+        "duration": "44.0",
         "name": "rendering.desktop/css_transitions_triggered_updating_class"
     },
     {
-        "duration": "44.0",
+        "duration": "42.0",
         "name": "rendering.desktop/css_transitions_updating_class"
     },
     {
@@ -2524,23 +2524,23 @@
         "name": "rendering.desktop/css_value_type_filter"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_value_type_length"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_value_type_length_complex"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_value_type_length_simple"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_value_type_path"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/css_value_type_shadow"
     },
     {
@@ -2552,15 +2552,15 @@
         "name": "rendering.desktop/css_value_type_transform_simple"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/docs_paper.html"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/docs_resume.html"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/docs_table.html"
     },
     {
@@ -2572,7 +2572,7 @@
         "name": "rendering.desktop/draw_image_not_pixel_aligned"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/dynamic_canvas_to_hw_accelerated_canvas.html"
     },
     {
@@ -2580,11 +2580,11 @@
         "name": "rendering.desktop/dynamic_cube_map"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/dynamic_webgl_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "44.0",
+        "duration": "45.0",
         "name": "rendering.desktop/earth"
     },
     {
@@ -2592,7 +2592,7 @@
         "name": "rendering.desktop/ebay_2018"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "rendering.desktop/ebay_pinch_2018"
     },
     {
@@ -2600,163 +2600,163 @@
         "name": "rendering.desktop/effect_games"
     },
     {
-        "duration": "60.0",
+        "duration": "58.0",
         "name": "rendering.desktop/espn_2018"
     },
     {
-        "duration": "47.0",
+        "duration": "48.0",
         "name": "rendering.desktop/espn_pinch_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "42.0",
         "name": "rendering.desktop/extra_large_texture_uploads"
     },
     {
-        "duration": "41.0",
+        "duration": "48.0",
         "name": "rendering.desktop/facebook_2018"
     },
     {
-        "duration": "44.0",
+        "duration": "45.0",
         "name": "rendering.desktop/facebook_pinch_2018"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/falling_particle_simulation_cpu.html"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/falling_particle_simulation_gpu.html"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/fill_clear_rect.html"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/fill_shapes"
     },
     {
-        "duration": "46.0",
+        "duration": "42.0",
         "name": "rendering.desktop/filter_terrain_svg"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "rendering.desktop/geo_apis"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/get_image_data_cpu.html"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/get_image_data_gpu.html"
     },
     {
-        "duration": "45.0",
+        "duration": "39.0",
         "name": "rendering.desktop/gmail_2018"
     },
     {
-        "duration": "60.0",
+        "duration": "54.0",
         "name": "rendering.desktop/gmail_move_2018"
     },
     {
-        "duration": "44.0",
+        "duration": "39.0",
         "name": "rendering.desktop/gmail_pinch_2018"
     },
     {
-        "duration": "37.0",
+        "duration": "34.0",
         "name": "rendering.desktop/google_calendar_2018"
     },
     {
-        "duration": "41.0",
+        "duration": "37.0",
         "name": "rendering.desktop/google_calendar_pinch_2018"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/google_docs_2018"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/google_image_pinch_2018"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/google_image_search_2018"
     },
     {
-        "duration": "40.0",
+        "duration": "37.0",
         "name": "rendering.desktop/google_plus_2018"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/google_search_pinch_2018"
     },
     {
-        "duration": "38.0",
+        "duration": "34.0",
         "name": "rendering.desktop/google_web_search_2018"
     },
     {
-        "duration": "42.0",
+        "duration": "38.0",
         "name": "rendering.desktop/gpu_bound_shader.html"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/guimark_vector_chart"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/hakim"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/hw_accelerated_canvas_to_sw_canvas.html"
     },
     {
-        "duration": "63.0",
+        "duration": "55.0",
         "name": "rendering.desktop/ie_chalkboard"
     },
     {
-        "duration": "50.0",
+        "duration": "73.0",
         "name": "rendering.desktop/ie_pirate_mark"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/infinite_scroll_element_n_layers_99"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/infinite_scroll_root_fixed_n_layers_99"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/infinite_scroll_root_n_layers_99"
     },
     {
-        "duration": "45.0",
+        "duration": "50.0",
         "name": "rendering.desktop/jarro_doverson"
     },
     {
-        "duration": "35.0",
+        "duration": "36.0",
         "name": "rendering.desktop/jpeg_decoding_rgb_and_gpu_rasterization"
     },
     {
-        "duration": "35.0",
+        "duration": "36.0",
         "name": "rendering.desktop/jpeg_decoding_yuv_and_gpu_rasterization"
     },
     {
-        "duration": "46.0",
+        "duration": "42.0",
         "name": "rendering.desktop/js_full_screen_invalidation"
     },
     {
-        "duration": "44.0",
+        "duration": "41.0",
         "name": "rendering.desktop/js_opacity_plus_n_layers_99"
     },
     {
-        "duration": "42.0",
+        "duration": "45.0",
         "name": "rendering.desktop/js_paint_plus_n_layers_99"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "rendering.desktop/js_poster_circle"
     },
     {
@@ -2764,11 +2764,11 @@
         "name": "rendering.desktop/js_scroll_text_only"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/kevs_3d"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/keyframed_animations"
     },
     {
@@ -2776,79 +2776,79 @@
         "name": "rendering.desktop/large_texture_uploads"
     },
     {
-        "duration": "37.0",
+        "duration": "38.0",
         "name": "rendering.desktop/linkedin_2018"
     },
     {
-        "duration": "45.0",
+        "duration": "46.0",
         "name": "rendering.desktop/linkedin_pinch_2018"
     },
     {
-        "duration": "84.0",
+        "duration": "85.0",
         "name": "rendering.desktop/lost_crypt"
     },
     {
-        "duration": "75.0",
+        "duration": "76.0",
         "name": "rendering.desktop/lost_crypt_fast_call"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_0fps_impl_60fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_0fps_impl_60fps_no_update"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_0fps_impl_60fps_no_update_jank"
     },
     {
-        "duration": "38.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_0fps_with_jank_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_15fps_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_15fps_with_jank_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_30fps_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_30fps_impl_60fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_impl_60fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_impl_60fps_no_update"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_impl_60fps_no_update_jank"
     },
     {
-        "duration": "53.0",
+        "duration": "54.0",
         "name": "rendering.desktop/main_60fps_with_extreme_jank_impl_0fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_with_jank_and_delay_impl_60fps"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/main_60fps_with_jank_impl_0fps"
     },
     {
@@ -2856,587 +2856,587 @@
         "name": "rendering.desktop/main_animations_half_presented"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.desktop/man_in_blue"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/many_images"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.desktop/many_planets_deep"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/maps_move_2018"
     },
     {
-        "duration": "41.0",
+        "duration": "75.0",
         "name": "rendering.desktop/maps_perf_test"
     },
     {
-        "duration": "39.0",
+        "duration": "69.0",
         "name": "rendering.desktop/medium_texture_uploads"
     },
     {
-        "duration": "39.0",
+        "duration": "73.0",
         "name": "rendering.desktop/megi_dish"
     },
     {
-        "duration": "73.0",
+        "duration": "131.0",
         "name": "rendering.desktop/microgame_fps"
     },
     {
-        "duration": "75.0",
+        "duration": "117.0",
         "name": "rendering.desktop/microgame_fps_fast_call"
     },
     {
-        "duration": "39.0",
+        "duration": "69.0",
         "name": "rendering.desktop/microsoft_asteroid_belt"
     },
     {
-        "duration": "40.0",
+        "duration": "69.0",
         "name": "rendering.desktop/microsoft_fireflies"
     },
     {
-        "duration": "39.0",
+        "duration": "69.0",
         "name": "rendering.desktop/microsoft_fish_ie_tank"
     },
     {
-        "duration": "39.0",
+        "duration": "68.0",
         "name": "rendering.desktop/microsoft_performance"
     },
     {
-        "duration": "39.0",
+        "duration": "71.0",
         "name": "rendering.desktop/microsoft_snow"
     },
     {
-        "duration": "39.0",
+        "duration": "70.0",
         "name": "rendering.desktop/microsoft_speed_reading"
     },
     {
-        "duration": "40.0",
+        "duration": "68.0",
         "name": "rendering.desktop/microsoft_tweet_map"
     },
     {
-        "duration": "41.0",
+        "duration": "74.0",
         "name": "rendering.desktop/microsoft_video_city"
     },
     {
-        "duration": "39.0",
+        "duration": "67.0",
         "name": "rendering.desktop/microsoft_worker_fountains"
     },
     {
-        "duration": "39.0",
+        "duration": "67.0",
         "name": "rendering.desktop/mix_10k"
     },
     {
-        "duration": "39.0",
+        "duration": "73.0",
         "name": "rendering.desktop/mix_blend_mode_animation_difference"
     },
     {
-        "duration": "39.0",
+        "duration": "72.0",
         "name": "rendering.desktop/mix_blend_mode_animation_hue"
     },
     {
-        "duration": "39.0",
+        "duration": "71.0",
         "name": "rendering.desktop/mix_blend_mode_animation_propagating_isolation"
     },
     {
-        "duration": "39.0",
+        "duration": "65.0",
         "name": "rendering.desktop/mix_blend_mode_animation_screen"
     },
     {
-        "duration": "44.0",
+        "duration": "68.0",
         "name": "rendering.desktop/motion_mark_canvas_fill_shapes"
     },
     {
-        "duration": "39.0",
+        "duration": "67.0",
         "name": "rendering.desktop/motion_mark_canvas_stroke_shapes"
     },
     {
-        "duration": "49.0",
+        "duration": "71.0",
         "name": "rendering.desktop/new_tilings"
     },
     {
-        "duration": "51.0",
+        "duration": "74.0",
         "name": "rendering.desktop/nvidia_vertex_buffer_object"
     },
     {
-        "duration": "48.0",
+        "duration": "68.0",
         "name": "rendering.desktop/off_screen_main_60fps"
     },
     {
-        "duration": "46.0",
+        "duration": "68.0",
         "name": "rendering.desktop/off_screen_main_60fps_jank"
     },
     {
-        "duration": "46.0",
+        "duration": "69.0",
         "name": "rendering.desktop/overlay_background_color_css_transitions_page"
     },
     {
-        "duration": "49.0",
+        "duration": "70.0",
         "name": "rendering.desktop/particles"
     },
     {
-        "duration": "56.0",
+        "duration": "85.0",
         "name": "rendering.desktop/pinterest_2018"
     },
     {
-        "duration": "48.0",
+        "duration": "68.0",
         "name": "rendering.desktop/put_and_create_imagebitmap_from_imagedata"
     },
     {
-        "duration": "46.0",
+        "duration": "45.0",
         "name": "rendering.desktop/put_get_image_data"
     },
     {
-        "duration": "47.0",
+        "duration": "43.0",
         "name": "rendering.desktop/put_image_data.html"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/raf"
     },
     {
-        "duration": "44.0",
+        "duration": "40.0",
         "name": "rendering.desktop/raf_animation"
     },
     {
-        "duration": "43.0",
+        "duration": "40.0",
         "name": "rendering.desktop/raf_canvas"
     },
     {
-        "duration": "44.0",
+        "duration": "39.0",
         "name": "rendering.desktop/raf_touch_animation"
     },
     {
-        "duration": "46.0",
+        "duration": "42.0",
         "name": "rendering.desktop/repaint_amazon_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_cnn_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_facebook_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_google_search_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_instagram_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_reddit_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_theverge_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_twitter_2018"
     },
     {
-        "duration": "42.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_wikipedia_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/repaint_yahoo_homepage_2018"
     },
     {
-        "duration": "47.0",
+        "duration": "46.0",
         "name": "rendering.desktop/runway_2019"
     },
     {
-        "duration": "49.0",
+        "duration": "46.0",
         "name": "rendering.desktop/san_angeles"
     },
     {
-        "duration": "37.0",
+        "duration": "44.0",
         "name": "rendering.desktop/second_batch_js_heavy"
     },
     {
-        "duration": "37.0",
+        "duration": "44.0",
         "name": "rendering.desktop/second_batch_js_light"
     },
     {
-        "duration": "37.0",
+        "duration": "44.0",
         "name": "rendering.desktop/second_batch_js_medium"
     },
     {
-        "duration": "44.0",
+        "duration": "50.0",
         "name": "rendering.desktop/sheets_render.html"
     },
     {
-        "duration": "40.0",
+        "duration": "47.0",
         "name": "rendering.desktop/simple_text_page"
     },
     {
-        "duration": "38.0",
+        "duration": "40.0",
         "name": "rendering.desktop/simple_touch_drag"
     },
     {
-        "duration": "87.0",
+        "duration": "94.0",
         "name": "rendering.desktop/skelebuddies_wasm_2020"
     },
     {
-        "duration": "76.0",
+        "duration": "83.0",
         "name": "rendering.desktop/skelebuddies_wasm_2020_fast_call"
     },
     {
-        "duration": "43.0",
+        "duration": "50.0",
         "name": "rendering.desktop/small_texture_uploads"
     },
     {
-        "duration": "49.0",
+        "duration": "60.0",
         "name": "rendering.desktop/smash_cat"
     },
     {
-        "duration": "43.0",
+        "duration": "50.0",
         "name": "rendering.desktop/spielzeugz"
     },
     {
-        "duration": "43.0",
+        "duration": "48.0",
         "name": "rendering.desktop/static_canvas_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "43.0",
+        "duration": "46.0",
         "name": "rendering.desktop/static_webgl_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/stroke_shapes"
     },
     {
-        "duration": "40.0",
+        "duration": "42.0",
         "name": "rendering.desktop/sync_scroll_offset"
     },
     {
-        "duration": "57.0",
+        "duration": "60.0",
         "name": "rendering.desktop/techcrunch_2018"
     },
     {
-        "duration": "55.0",
+        "duration": "52.0",
         "name": "rendering.desktop/text_05000_pixels_per_second"
     },
     {
-        "duration": "54.0",
+        "duration": "50.0",
         "name": "rendering.desktop/text_10000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "50.0",
         "name": "rendering.desktop/text_20000_pixels_per_second"
     },
     {
-        "duration": "51.0",
+        "duration": "48.0",
         "name": "rendering.desktop/text_40000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "58.0",
         "name": "rendering.desktop/text_60000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_75000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_90000_pixels_per_second"
     },
     {
-        "duration": "57.0",
+        "duration": "61.0",
         "name": "rendering.desktop/text_constant_full_page_raster_05000_pixels_per_second"
     },
     {
-        "duration": "55.0",
+        "duration": "59.0",
         "name": "rendering.desktop/text_constant_full_page_raster_10000_pixels_per_second"
     },
     {
-        "duration": "53.0",
+        "duration": "57.0",
         "name": "rendering.desktop/text_constant_full_page_raster_20000_pixels_per_second"
     },
     {
-        "duration": "53.0",
+        "duration": "57.0",
         "name": "rendering.desktop/text_constant_full_page_raster_40000_pixels_per_second"
     },
     {
-        "duration": "50.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_constant_full_page_raster_60000_pixels_per_second"
     },
     {
-        "duration": "49.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_constant_full_page_raster_75000_pixels_per_second"
     },
     {
-        "duration": "49.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_constant_full_page_raster_90000_pixels_per_second"
     },
     {
-        "duration": "56.0",
+        "duration": "60.0",
         "name": "rendering.desktop/text_hover_05000_pixels_per_second"
     },
     {
-        "duration": "54.0",
+        "duration": "58.0",
         "name": "rendering.desktop/text_hover_10000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_hover_20000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_hover_40000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_hover_60000_pixels_per_second"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_hover_75000_pixels_per_second"
     },
     {
-        "duration": "51.0",
+        "duration": "56.0",
         "name": "rendering.desktop/text_hover_90000_pixels_per_second"
     },
     {
-        "duration": "47.0",
+        "duration": "49.0",
         "name": "rendering.desktop/throughput_scrolling_active_handler"
     },
     {
-        "duration": "46.0",
+        "duration": "49.0",
         "name": "rendering.desktop/throughput_scrolling_composited"
     },
     {
-        "duration": "47.0",
+        "duration": "49.0",
         "name": "rendering.desktop/throughput_scrolling_passive_handler"
     },
     {
-        "duration": "46.0",
+        "duration": "49.0",
         "name": "rendering.desktop/throughput_scrolling_uncomposited"
     },
     {
-        "duration": "77.0",
+        "duration": "89.0",
         "name": "rendering.desktop/tiny_racing_v3_wasm_2020"
     },
     {
-        "duration": "77.0",
+        "duration": "79.0",
         "name": "rendering.desktop/tiny_racing_v3_wasm_2020_fast_call"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/toBlob_duration.html"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/toBlob_duration_jpeg.html"
     },
     {
-        "duration": "45.0",
+        "duration": "47.0",
         "name": "rendering.desktop/toBlob_small_canvas_in_worker.html"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "rendering.desktop/touch_handler_scrolling"
     },
     {
-        "duration": "43.0",
+        "duration": "45.0",
         "name": "rendering.desktop/transfer_from_imageBitmap.html"
     },
     {
-        "duration": "43.0",
+        "duration": "45.0",
         "name": "rendering.desktop/transform_transitions"
     },
     {
-        "duration": "43.0",
+        "duration": "45.0",
         "name": "rendering.desktop/transform_transitions_js_block"
     },
     {
-        "duration": "43.0",
+        "duration": "45.0",
         "name": "rendering.desktop/twitch_2018"
     },
     {
-        "duration": "53.0",
+        "duration": "55.0",
         "name": "rendering.desktop/twitch_pinch_2018"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/twitter_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "45.0",
         "name": "rendering.desktop/twitter_pinch_2018"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/video_to_hw_accelerated_canvas"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/video_to_sub_texture"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/video_to_sub_texture_flip_and_premultiply"
     },
     {
-        "duration": "44.0",
+        "duration": "46.0",
         "name": "rendering.desktop/video_to_sub_texture_flip_y"
     },
     {
-        "duration": "44.0",
+        "duration": "38.0",
         "name": "rendering.desktop/video_to_sub_texture_premultiply"
     },
     {
-        "duration": "45.0",
+        "duration": "37.0",
         "name": "rendering.desktop/video_to_texture"
     },
     {
-        "duration": "43.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animation_value_type_color"
     },
     {
-        "duration": "43.0",
+        "duration": "37.0",
         "name": "rendering.desktop/web_animation_value_type_length_3d"
     },
     {
-        "duration": "44.0",
+        "duration": "37.0",
         "name": "rendering.desktop/web_animation_value_type_length_complex"
     },
     {
-        "duration": "43.0",
+        "duration": "37.0",
         "name": "rendering.desktop/web_animation_value_type_length_simple"
     },
     {
-        "duration": "41.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animation_value_type_path"
     },
     {
-        "duration": "40.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animation_value_type_shadow"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animation_value_type_transform_complex"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animation_value_type_transform_simple"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.desktop/web_animations_many_keyframes"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animations_set_current_time"
     },
     {
-        "duration": "39.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animations_simultaneous"
     },
     {
-        "duration": "40.0",
+        "duration": "37.0",
         "name": "rendering.desktop/web_animations_staggered_chaining"
     },
     {
-        "duration": "43.0",
+        "duration": "36.0",
         "name": "rendering.desktop/web_animations_staggered_infinite_iterations"
     },
     {
-        "duration": "40.0",
+        "duration": "37.0",
         "name": "rendering.desktop/web_animations_staggered_triggering_page"
     },
     {
-        "duration": "40.0",
+        "duration": "37.0",
         "name": "rendering.desktop/webgl_to_texture"
     },
     {
-        "duration": "35.0",
+        "duration": "32.0",
         "name": "rendering.desktop/webp_decoding_rgb_and_gpu_rasterization"
     },
     {
-        "duration": "35.0",
+        "duration": "32.0",
         "name": "rendering.desktop/webp_decoding_yuv_and_gpu_rasterization"
     },
     {
-        "duration": "54.0",
+        "duration": "44.0",
         "name": "rendering.desktop/wikipedia_2018"
     },
     {
-        "duration": "40.0",
+        "duration": "36.0",
         "name": "rendering.desktop/wordpress_2018"
     },
     {
-        "duration": "35.0",
+        "duration": "32.0",
         "name": "rendering.desktop/yahoo_answers_2018"
     },
     {
-        "duration": "38.0",
+        "duration": "35.0",
         "name": "rendering.desktop/yahoo_news_2018"
     },
     {
-        "duration": "46.0",
+        "duration": "41.0",
         "name": "rendering.desktop/yahoo_news_pinch_2018"
     },
     {
-        "duration": "40.0",
+        "duration": "36.0",
         "name": "rendering.desktop/yahoo_sports_2018"
     },
     {
-        "duration": "41.0",
+        "duration": "37.0",
         "name": "rendering.desktop/yahoo_sports_pinch_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.desktop/youtube_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.desktop/youtube_pinch_2018"
     },
     {
-        "duration": "82.0",
+        "duration": "73.0",
         "name": "speedometer-future/http://browserbench.org/Speedometer/"
     },
     {
-        "duration": "80.0",
+        "duration": "67.0",
         "name": "speedometer/http://browserbench.org/Speedometer/"
     },
     {
-        "duration": "149.0",
+        "duration": "124.0",
         "name": "speedometer2-future/Speedometer2"
     },
     {
-        "duration": "180.0",
+        "duration": "152.0",
         "name": "speedometer2-pcscan/Speedometer2"
     },
     {
-        "duration": "142.0",
+        "duration": "120.0",
         "name": "speedometer2/Speedometer2"
     },
     {
-        "duration": "61.0",
+        "duration": "60.0",
         "name": "system_health.common_desktop/browse:media:googleplaystore:2018"
     },
     {
-        "duration": "100.0",
+        "duration": "99.0",
         "name": "system_health.common_desktop/browse:media:imgur"
     },
     {
-        "duration": "124.0",
+        "duration": "113.0",
         "name": "system_health.common_desktop/browse:media:pinterest:2018"
     },
     {
-        "duration": "99.0",
+        "duration": "97.0",
         "name": "system_health.common_desktop/browse:media:tumblr:2018"
     },
     {
@@ -3444,99 +3444,99 @@
         "name": "system_health.common_desktop/browse:media:youtube:2019"
     },
     {
-        "duration": "96.0",
+        "duration": "95.0",
         "name": "system_health.common_desktop/browse:media:youtubetv:2019"
     },
     {
-        "duration": "105.0",
+        "duration": "104.0",
         "name": "system_health.common_desktop/browse:media:youtubetv_watch:2020"
     },
     {
-        "duration": "101.0",
+        "duration": "94.0",
         "name": "system_health.common_desktop/browse:news:cnn:2020"
     },
     {
-        "duration": "78.0",
+        "duration": "73.0",
         "name": "system_health.common_desktop/browse:news:flipboard:2020"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/browse:news:hackernews:2020"
     },
     {
-        "duration": "120.0",
+        "duration": "106.0",
         "name": "system_health.common_desktop/browse:news:nytimes:2020"
     },
     {
-        "duration": "98.0",
+        "duration": "93.0",
         "name": "system_health.common_desktop/browse:news:reddit:2020"
     },
     {
-        "duration": "69.0",
+        "duration": "68.0",
         "name": "system_health.common_desktop/browse:search:google:2020"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "system_health.common_desktop/browse:search:google_india:2018"
     },
     {
-        "duration": "104.0",
+        "duration": "99.0",
         "name": "system_health.common_desktop/browse:social:facebook_infinite_scroll:2018"
     },
     {
-        "duration": "97.0",
+        "duration": "94.0",
         "name": "system_health.common_desktop/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "80.0",
+        "duration": "78.0",
         "name": "system_health.common_desktop/browse:social:twitter:2018"
     },
     {
-        "duration": "93.0",
+        "duration": "92.0",
         "name": "system_health.common_desktop/browse:social:twitter_infinite_scroll:2018"
     },
     {
-        "duration": "88.0",
+        "duration": "84.0",
         "name": "system_health.common_desktop/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "118.0",
+        "duration": "30.0",
         "name": "system_health.common_desktop/browse:tools:autocad:2021"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "system_health.common_desktop/browse:tools:docs_scrolling"
     },
     {
-        "duration": "137.0",
+        "duration": "128.0",
         "name": "system_health.common_desktop/browse:tools:earth:2020"
     },
     {
-        "duration": "33.0",
+        "duration": "30.0",
         "name": "system_health.common_desktop/browse:tools:gmail-compose:2020"
     },
     {
-        "duration": "33.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/browse:tools:gmail-labelclick:2020"
     },
     {
-        "duration": "33.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/browse:tools:gmail-openconversation:2020"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/browse:tools:gmail-search:2020"
     },
     {
-        "duration": "94.0",
+        "duration": "93.0",
         "name": "system_health.common_desktop/browse:tools:maps:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/browse:tools:sheets:2019"
     },
     {
-        "duration": "33.0",
+        "duration": "30.0",
         "name": "system_health.common_desktop/browse_accessibility:media:youtube"
     },
     {
@@ -3544,279 +3544,279 @@
         "name": "system_health.common_desktop/browse_accessibility:tech:codesearch:2018"
     },
     {
-        "duration": "46.0",
+        "duration": "44.0",
         "name": "system_health.common_desktop/load:chrome:blank"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:games:alphabetty:2018"
     },
     {
-        "duration": "36.0",
+        "duration": "35.0",
         "name": "system_health.common_desktop/load:games:bubbles:2020"
     },
     {
-        "duration": "36.0",
+        "duration": "42.0",
         "name": "system_health.common_desktop/load:games:lazors"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "system_health.common_desktop/load:games:miniclip:2018"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "system_health.common_desktop/load:games:spychase:2018"
     },
     {
-        "duration": "45.0",
+        "duration": "44.0",
         "name": "system_health.common_desktop/load:media:9gag"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:media:dailymotion:2019"
     },
     {
-        "duration": "40.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:media:facebook_feed:desktop:2020"
     },
     {
-        "duration": "40.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:media:facebook_photos:2018"
     },
     {
-        "duration": "40.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:media:facebook_photos:desktop:2020"
     },
     {
-        "duration": "39.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:media:flickr:2018"
     },
     {
-        "duration": "37.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:media:google_images:2018"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "system_health.common_desktop/load:media:imgur:2018"
     },
     {
-        "duration": "41.0",
+        "duration": "42.0",
         "name": "system_health.common_desktop/load:media:soundcloud:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:media:youtube:2018"
     },
     {
-        "duration": "37.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:media:youtubelivingroom:2020"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:news:bbc:2018"
     },
     {
-        "duration": "39.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:news:cnn:2020"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:news:flipboard"
     },
     {
-        "duration": "36.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:news:hackernews:2018"
     },
     {
-        "duration": "43.0",
+        "duration": "44.0",
         "name": "system_health.common_desktop/load:news:nytimes:2018"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "system_health.common_desktop/load:news:qq:2018"
     },
     {
-        "duration": "40.0",
+        "duration": "41.0",
         "name": "system_health.common_desktop/load:news:reddit:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "40.0",
         "name": "system_health.common_desktop/load:news:wikipedia:2018"
     },
     {
-        "duration": "37.0",
+        "duration": "36.0",
         "name": "system_health.common_desktop/load:search:amazon:2018"
     },
     {
-        "duration": "37.0",
+        "duration": "36.0",
         "name": "system_health.common_desktop/load:search:baidu:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:search:ebay:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:search:flipkart:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:search:google:2018"
     },
     {
-        "duration": "37.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:search:taobao:2018"
     },
     {
-        "duration": "36.0",
+        "duration": "38.0",
         "name": "system_health.common_desktop/load:search:yahoo:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:search:yandex:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:social:instagram:2018"
     },
     {
-        "duration": "40.0",
+        "duration": "41.0",
         "name": "system_health.common_desktop/load:social:pinterest:2019"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:social:vk:2018"
     },
     {
-        "duration": "50.0",
+        "duration": "48.0",
         "name": "system_health.common_desktop/load:tools:chat:2020"
     },
     {
-        "duration": "45.0",
+        "duration": "43.0",
         "name": "system_health.common_desktop/load:tools:docs:2019"
     },
     {
-        "duration": "39.0",
+        "duration": "37.0",
         "name": "system_health.common_desktop/load:tools:drive:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_desktop/load:tools:gmail:2019"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:tools:stackoverflow:2018"
     },
     {
-        "duration": "38.0",
+        "duration": "39.0",
         "name": "system_health.common_desktop/load:tools:weather:2019"
     },
     {
-        "duration": "48.0",
+        "duration": "45.0",
         "name": "system_health.common_desktop/load_accessibility:media:wikipedia:2018"
     },
     {
-        "duration": "48.0",
+        "duration": "45.0",
         "name": "system_health.common_desktop/load_accessibility:shopping:amazon:2018"
     },
     {
-        "duration": "146.0",
+        "duration": "147.0",
         "name": "system_health.common_desktop/long_running:tools:gmail-background"
     },
     {
-        "duration": "34.0",
+        "duration": "25.0",
         "name": "system_health.common_desktop/long_running:tools:gmail-foreground"
     },
     {
-        "duration": "34.0",
+        "duration": "25.0",
         "name": "system_health.common_desktop/multitab:misc:typical24"
     },
     {
-        "duration": "34.0",
+        "duration": "25.0",
         "name": "system_health.common_desktop/multitab:misc:typical24:2018"
     },
     {
-        "duration": "66.0",
+        "duration": "65.0",
         "name": "system_health.common_desktop/play:media:google_play_music"
     },
     {
-        "duration": "76.0",
+        "duration": "75.0",
         "name": "system_health.common_desktop/play:media:soundcloud:2018"
     },
     {
-        "duration": "48.0",
+        "duration": "46.0",
         "name": "system_health.memory_desktop/browse:media:googleplaystore:2018"
     },
     {
-        "duration": "85.0",
+        "duration": "81.0",
         "name": "system_health.memory_desktop/browse:media:imgur"
     },
     {
-        "duration": "97.0",
+        "duration": "93.0",
         "name": "system_health.memory_desktop/browse:media:pinterest:2018"
     },
     {
-        "duration": "72.0",
+        "duration": "69.0",
         "name": "system_health.memory_desktop/browse:media:tumblr:2018"
     },
     {
-        "duration": "11.0",
+        "duration": "7.0",
         "name": "system_health.memory_desktop/browse:media:youtube:2019"
     },
     {
-        "duration": "69.0",
+        "duration": "65.0",
         "name": "system_health.memory_desktop/browse:media:youtubetv:2019"
     },
     {
-        "duration": "78.0",
+        "duration": "74.0",
         "name": "system_health.memory_desktop/browse:media:youtubetv_watch:2020"
     },
     {
-        "duration": "70.0",
+        "duration": "65.0",
         "name": "system_health.memory_desktop/browse:news:cnn:2020"
     },
     {
-        "duration": "50.0",
+        "duration": "47.0",
         "name": "system_health.memory_desktop/browse:news:flipboard:2020"
     },
     {
-        "duration": "59.0",
+        "duration": "57.0",
         "name": "system_health.memory_desktop/browse:news:hackernews:2020"
     },
     {
-        "duration": "88.0",
+        "duration": "80.0",
         "name": "system_health.memory_desktop/browse:news:nytimes:2020"
     },
     {
-        "duration": "71.0",
+        "duration": "65.0",
         "name": "system_health.memory_desktop/browse:news:reddit:2020"
     },
     {
-        "duration": "56.0",
+        "duration": "51.0",
         "name": "system_health.memory_desktop/browse:search:google:2020"
     },
     {
-        "duration": "40.0",
+        "duration": "35.0",
         "name": "system_health.memory_desktop/browse:search:google_india:2018"
     },
     {
-        "duration": "75.0",
+        "duration": "73.0",
         "name": "system_health.memory_desktop/browse:social:facebook_infinite_scroll:2018"
     },
     {
-        "duration": "11.0",
+        "duration": "9.0",
         "name": "system_health.memory_desktop/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "53.0",
+        "duration": "50.0",
         "name": "system_health.memory_desktop/browse:social:twitter:2018"
     },
     {
-        "duration": "11.0",
+        "duration": "7.0",
         "name": "system_health.memory_desktop/browse:social:twitter_infinite_scroll:2018"
     },
     {
-        "duration": "61.0",
+        "duration": "58.0",
         "name": "system_health.memory_desktop/browse:tech:discourse_infinite_scroll:2018"
     },
     {
@@ -3824,35 +3824,35 @@
         "name": "system_health.memory_desktop/browse:tools:autocad:2021"
     },
     {
-        "duration": "43.0",
+        "duration": "37.0",
         "name": "system_health.memory_desktop/browse:tools:docs_scrolling"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "system_health.memory_desktop/browse:tools:earth:2020"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "system_health.memory_desktop/browse:tools:gmail-compose:2020"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "system_health.memory_desktop/browse:tools:gmail-labelclick:2020"
     },
     {
-        "duration": "12.0",
+        "duration": "8.0",
         "name": "system_health.memory_desktop/browse:tools:gmail-openconversation:2020"
     },
     {
-        "duration": "12.0",
+        "duration": "8.0",
         "name": "system_health.memory_desktop/browse:tools:gmail-search:2020"
     },
     {
-        "duration": "82.0",
+        "duration": "75.0",
         "name": "system_health.memory_desktop/browse:tools:maps:2019"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "system_health.memory_desktop/browse:tools:sheets:2019"
     },
     {
@@ -3860,7 +3860,7 @@
         "name": "system_health.memory_desktop/browse_accessibility:media:youtube"
     },
     {
-        "duration": "32.0",
+        "duration": "31.0",
         "name": "system_health.memory_desktop/browse_accessibility:tech:codesearch:2018"
     },
     {
@@ -3868,135 +3868,135 @@
         "name": "system_health.memory_desktop/load:chrome:blank"
     },
     {
-        "duration": "27.0",
+        "duration": "24.0",
         "name": "system_health.memory_desktop/load:games:alphabetty:2018"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "system_health.memory_desktop/load:games:bubbles:2020"
     },
     {
-        "duration": "25.0",
+        "duration": "28.0",
         "name": "system_health.memory_desktop/load:games:lazors"
     },
     {
-        "duration": "28.0",
+        "duration": "31.0",
         "name": "system_health.memory_desktop/load:games:miniclip:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:games:spychase:2018"
     },
     {
-        "duration": "30.0",
+        "duration": "27.0",
         "name": "system_health.memory_desktop/load:media:9gag"
     },
     {
-        "duration": "27.0",
+        "duration": "25.0",
         "name": "system_health.memory_desktop/load:media:dailymotion:2019"
     },
     {
-        "duration": "28.0",
+        "duration": "31.0",
         "name": "system_health.memory_desktop/load:media:facebook_feed:desktop:2020"
     },
     {
-        "duration": "27.0",
+        "duration": "30.0",
         "name": "system_health.memory_desktop/load:media:facebook_photos:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "31.0",
         "name": "system_health.memory_desktop/load:media:facebook_photos:desktop:2020"
     },
     {
-        "duration": "26.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:media:flickr:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "28.0",
         "name": "system_health.memory_desktop/load:media:google_images:2018"
     },
     {
-        "duration": "26.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:media:imgur:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:media:soundcloud:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:media:youtube:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:media:youtubelivingroom:2020"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "system_health.memory_desktop/load:news:bbc:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "25.0",
         "name": "system_health.memory_desktop/load:news:cnn:2020"
     },
     {
-        "duration": "25.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:news:flipboard"
     },
     {
-        "duration": "23.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:news:hackernews:2018"
     },
     {
-        "duration": "30.0",
+        "duration": "33.0",
         "name": "system_health.memory_desktop/load:news:nytimes:2018"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "system_health.memory_desktop/load:news:qq:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:news:reddit:2018"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "system_health.memory_desktop/load:news:wikipedia:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "23.0",
         "name": "system_health.memory_desktop/load:search:amazon:2018"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "system_health.memory_desktop/load:search:baidu:2018"
     },
     {
-        "duration": "26.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:search:ebay:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "28.0",
         "name": "system_health.memory_desktop/load:search:flipkart:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "28.0",
         "name": "system_health.memory_desktop/load:search:google:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:search:taobao:2018"
     },
     {
-        "duration": "24.0",
+        "duration": "25.0",
         "name": "system_health.memory_desktop/load:search:yahoo:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:search:yandex:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "28.0",
         "name": "system_health.memory_desktop/load:social:instagram:2018"
     },
     {
@@ -4004,63 +4004,63 @@
         "name": "system_health.memory_desktop/load:social:pinterest:2019"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:social:vk:2018"
     },
     {
-        "duration": "36.0",
+        "duration": "33.0",
         "name": "system_health.memory_desktop/load:tools:chat:2020"
     },
     {
-        "duration": "32.0",
+        "duration": "34.0",
         "name": "system_health.memory_desktop/load:tools:docs:2019"
     },
     {
-        "duration": "27.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:tools:drive:2019"
     },
     {
-        "duration": "12.0",
+        "duration": "15.0",
         "name": "system_health.memory_desktop/load:tools:gmail:2019"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:tools:stackoverflow:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "system_health.memory_desktop/load:tools:weather:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.memory_desktop/load_accessibility:media:wikipedia:2018"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "system_health.memory_desktop/load_accessibility:shopping:amazon:2018"
     },
     {
-        "duration": "11.0",
+        "duration": "13.0",
         "name": "system_health.memory_desktop/long_running:tools:gmail-background"
     },
     {
-        "duration": "145.0",
+        "duration": "144.0",
         "name": "system_health.memory_desktop/long_running:tools:gmail-foreground"
     },
     {
-        "duration": "11.0",
+        "duration": "9.0",
         "name": "system_health.memory_desktop/multitab:misc:typical24"
     },
     {
-        "duration": "11.0",
+        "duration": "9.0",
         "name": "system_health.memory_desktop/multitab:misc:typical24:2018"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "system_health.memory_desktop/play:media:google_play_music"
     },
     {
-        "duration": "50.0",
+        "duration": "47.0",
         "name": "system_health.memory_desktop/play:media:soundcloud:2018"
     },
     {
@@ -4072,15 +4072,15 @@
         "name": "tracing.tracing_with_background_memory_infra/Facebook"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "tracing.tracing_with_background_memory_infra/Wikipedia"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.amazon.com"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.ask.com/"
     },
     {
@@ -4088,7 +4088,7 @@
         "name": "tracing.tracing_with_background_memory_infra/http://www.bing.com/"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.yahoo.com/"
     },
     {
@@ -4096,343 +4096,343 @@
         "name": "tracing.tracing_with_background_memory_infra/http://www.youtube.com"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/#hl=en&q=barack+obama"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/calendar/"
     },
     {
-        "duration": "116.0",
+        "duration": "64.0",
         "name": "v8.browsing_desktop-future/browse:media:googleplaystore:2018"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:media:imgur"
     },
     {
-        "duration": "173.0",
+        "duration": "115.0",
         "name": "v8.browsing_desktop-future/browse:media:pinterest:2018"
     },
     {
-        "duration": "159.0",
+        "duration": "153.0",
         "name": "v8.browsing_desktop-future/browse:media:tumblr:2018"
     },
     {
-        "duration": "68.0",
+        "duration": "85.0",
         "name": "v8.browsing_desktop-future/browse:media:youtube:2019"
     },
     {
-        "duration": "145.0",
+        "duration": "148.0",
         "name": "v8.browsing_desktop-future/browse:media:youtubetv:2019"
     },
     {
-        "duration": "149.0",
+        "duration": "157.0",
         "name": "v8.browsing_desktop-future/browse:media:youtubetv_watch:2020"
     },
     {
-        "duration": "60.0",
+        "duration": "62.0",
         "name": "v8.browsing_desktop-future/browse:news:cnn:2020"
     },
     {
-        "duration": "136.0",
+        "duration": "110.0",
         "name": "v8.browsing_desktop-future/browse:news:flipboard:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:news:hackernews:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:news:nytimes:2020"
     },
     {
-        "duration": "182.0",
+        "duration": "91.0",
         "name": "v8.browsing_desktop-future/browse:news:reddit:2020"
     },
     {
-        "duration": "128.0",
+        "duration": "77.0",
         "name": "v8.browsing_desktop-future/browse:search:google:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:search:google_india:2018"
     },
     {
-        "duration": "177.0",
+        "duration": "141.0",
         "name": "v8.browsing_desktop-future/browse:social:facebook_infinite_scroll:2018"
     },
     {
-        "duration": "146.0",
+        "duration": "150.0",
         "name": "v8.browsing_desktop-future/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "126.0",
+        "duration": "131.0",
         "name": "v8.browsing_desktop-future/browse:social:twitter:2018"
     },
     {
-        "duration": "144.0",
+        "duration": "145.0",
         "name": "v8.browsing_desktop-future/browse:social:twitter_infinite_scroll:2018"
     },
     {
-        "duration": "116.0",
+        "duration": "118.0",
         "name": "v8.browsing_desktop-future/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "118.0",
+        "duration": "62.0",
         "name": "v8.browsing_desktop-future/browse:tools:autocad:2021"
     },
     {
-        "duration": "110.0",
+        "duration": "57.0",
         "name": "v8.browsing_desktop-future/browse:tools:docs_scrolling"
     },
     {
-        "duration": "73.0",
+        "duration": "64.0",
         "name": "v8.browsing_desktop-future/browse:tools:earth:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:gmail-compose:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:gmail-labelclick:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:gmail-openconversation:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:gmail-search:2020"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:maps:2019"
     },
     {
-        "duration": "68.0",
+        "duration": "25.0",
         "name": "v8.browsing_desktop-future/browse:tools:sheets:2019"
     },
     {
-        "duration": "86.0",
+        "duration": "81.0",
         "name": "v8.browsing_desktop/browse:media:googleplaystore:2018"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:media:imgur"
     },
     {
-        "duration": "137.0",
+        "duration": "131.0",
         "name": "v8.browsing_desktop/browse:media:pinterest:2018"
     },
     {
-        "duration": "113.0",
+        "duration": "150.0",
         "name": "v8.browsing_desktop/browse:media:tumblr:2018"
     },
     {
-        "duration": "48.0",
+        "duration": "84.0",
         "name": "v8.browsing_desktop/browse:media:youtube:2019"
     },
     {
-        "duration": "110.0",
+        "duration": "146.0",
         "name": "v8.browsing_desktop/browse:media:youtubetv:2019"
     },
     {
-        "duration": "119.0",
+        "duration": "156.0",
         "name": "v8.browsing_desktop/browse:media:youtubetv_watch:2020"
     },
     {
-        "duration": "94.0",
+        "duration": "69.0",
         "name": "v8.browsing_desktop/browse:news:cnn:2020"
     },
     {
-        "duration": "91.0",
+        "duration": "85.0",
         "name": "v8.browsing_desktop/browse:news:flipboard:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:news:hackernews:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:news:nytimes:2020"
     },
     {
-        "duration": "112.0",
+        "duration": "104.0",
         "name": "v8.browsing_desktop/browse:news:reddit:2020"
     },
     {
-        "duration": "95.0",
+        "duration": "89.0",
         "name": "v8.browsing_desktop/browse:search:google:2020"
     },
     {
-        "duration": "78.0",
+        "duration": "72.0",
         "name": "v8.browsing_desktop/browse:search:google_india:2018"
     },
     {
-        "duration": "128.0",
+        "duration": "119.0",
         "name": "v8.browsing_desktop/browse:social:facebook_infinite_scroll:2018"
     },
     {
-        "duration": "106.0",
+        "duration": "147.0",
         "name": "v8.browsing_desktop/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "93.0",
+        "duration": "129.0",
         "name": "v8.browsing_desktop/browse:social:twitter:2018"
     },
     {
-        "duration": "106.0",
+        "duration": "143.0",
         "name": "v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018"
     },
     {
-        "duration": "155.0",
+        "duration": "129.0",
         "name": "v8.browsing_desktop/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "161.0",
+        "duration": "69.0",
         "name": "v8.browsing_desktop/browse:tools:autocad:2021"
     },
     {
-        "duration": "80.0",
+        "duration": "74.0",
         "name": "v8.browsing_desktop/browse:tools:docs_scrolling"
     },
     {
-        "duration": "195.0",
+        "duration": "168.0",
         "name": "v8.browsing_desktop/browse:tools:earth:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:tools:gmail-compose:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:tools:gmail-labelclick:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:tools:gmail-openconversation:2020"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:tools:gmail-search:2020"
     },
     {
-        "duration": "121.0",
+        "duration": "115.0",
         "name": "v8.browsing_desktop/browse:tools:maps:2019"
     },
     {
-        "duration": "48.0",
+        "duration": "43.0",
         "name": "v8.browsing_desktop/browse:tools:sheets:2019"
     },
     {
-        "duration": "44.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/AdsAMPAds_cold"
     },
     {
-        "duration": "50.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/AdsAMPAds_hot"
     },
     {
-        "duration": "46.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/AdsAMPAds_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/AdsAdSenseAsyncAds_cold"
     },
     {
-        "duration": "50.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/AdsAdSenseAsyncAds_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/AdsAdSenseAsyncAds_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/AdsAsyncAdSenseImage_cold"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/AdsAsyncAdSenseImage_hot"
     },
     {
-        "duration": "46.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/AdsAsyncAdSenseImage_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "43.0",
         "name": "v8.runtime_stats.top_25/AdsDoubleClickAsyncAds_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/AdsDoubleClickAsyncAds_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/AdsDoubleClickAsyncAds_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/AdsMultipleAdSlots_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/AdsMultipleAdSlots_hot"
     },
     {
-        "duration": "46.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/AdsMultipleAdSlots_warm"
     },
     {
-        "duration": "43.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/AdsOnScreenDetection_cold"
     },
     {
-        "duration": "48.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/AdsOnScreenDetection_hot"
     },
     {
-        "duration": "46.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/AdsOnScreenDetection_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "43.0",
         "name": "v8.runtime_stats.top_25/AdsSyncAdSenseImage_cold"
     },
     {
-        "duration": "50.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/AdsSyncAdSenseImage_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/AdsSyncAdSenseImage_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/AdsSyncLoadAsyncRenderAdSenseImage_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/AdsSyncLoadAsyncRenderAdSenseImage_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/AdsSyncLoadAsyncRenderAdSenseImage_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "55.0",
         "name": "v8.runtime_stats.top_25/AdsViewOptimizedRendering_cold"
     },
     {
-        "duration": "71.0",
+        "duration": "76.0",
         "name": "v8.runtime_stats.top_25/AdsViewOptimizedRendering_hot"
     },
     {
-        "duration": "62.0",
+        "duration": "66.0",
         "name": "v8.runtime_stats.top_25/AdsViewOptimizedRendering_warm"
     },
     {
@@ -4440,7 +4440,7 @@
         "name": "v8.runtime_stats.top_25/http://edition.cnn.com_cold"
     },
     {
-        "duration": "58.0",
+        "duration": "59.0",
         "name": "v8.runtime_stats.top_25/http://edition.cnn.com_hot"
     },
     {
@@ -4448,7 +4448,7 @@
         "name": "v8.runtime_stats.top_25/http://edition.cnn.com_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/http://hi.wikipedia.org/wiki/%E0%A4%AE%E0%A5%81%E0%A4%96%E0%A4%AA%E0%A5%83%E0%A4%B7%E0%A5%8D%E0%A4%A0_cold"
     },
     {
@@ -4472,7 +4472,7 @@
         "name": "v8.runtime_stats.top_25/http://inbox.google.com_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://maps.google.co.jp/maps/search/restaurant+tokyo_cold"
     },
     {
@@ -4488,71 +4488,71 @@
         "name": "v8.runtime_stats.top_25/http://meta.discourse.org_cold"
     },
     {
-        "duration": "57.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/http://meta.discourse.org_hot"
     },
     {
-        "duration": "54.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/http://meta.discourse.org_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?angular_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?angular_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?angular_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?backbone_cold"
     },
     {
-        "duration": "58.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?backbone_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "44.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?backbone_warm"
     },
     {
-        "duration": "43.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?ember_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?ember_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "44.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?ember_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?jquery_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?jquery_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?jquery_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?vanilla_cold"
     },
     {
-        "duration": "49.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?vanilla_hot"
     },
     {
-        "duration": "46.0",
+        "duration": "44.0",
         "name": "v8.runtime_stats.top_25/http://pollouer.muc/Speedometer/CustomRunner.html?vanilla_warm"
     },
     {
@@ -4568,15 +4568,15 @@
         "name": "v8.runtime_stats.top_25/http://reddit.musicplayer.io_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "58.0",
         "name": "v8.runtime_stats.top_25/http://weibo.com_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/http://weibo.com_hot"
     },
     {
-        "duration": "52.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/http://weibo.com_warm"
     },
     {
@@ -4588,43 +4588,43 @@
         "name": "v8.runtime_stats.top_25/http://world.taobao.com_hot"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/http://world.taobao.com_warm"
     },
     {
-        "duration": "46.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/http://www.amazon.com/s/?field-keywords=v8_cold"
     },
     {
-        "duration": "52.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/http://www.amazon.com/s/?field-keywords=v8_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.amazon.com/s/?field-keywords=v8_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "45.0",
         "name": "v8.runtime_stats.top_25/http://www.baidu.com/s?wd=v8_cold"
     },
     {
-        "duration": "52.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/http://www.baidu.com/s?wd=v8_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.baidu.com/s?wd=v8_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/http://www.bing.com/search?q=v8+engine_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "v8.runtime_stats.top_25/http://www.bing.com/search?q=v8+engine_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.bing.com/search?q=v8+engine_warm"
     },
     {
@@ -4632,7 +4632,7 @@
         "name": "v8.runtime_stats.top_25/http://www.ebay.fr/sch/i.html?_nkw=v8_cold"
     },
     {
-        "duration": "56.0",
+        "duration": "55.0",
         "name": "v8.runtime_stats.top_25/http://www.ebay.fr/sch/i.html?_nkw=v8_hot"
     },
     {
@@ -4640,75 +4640,75 @@
         "name": "v8.runtime_stats.top_25/http://www.ebay.fr/sch/i.html?_nkw=v8_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/http://www.instagram.com/archdigest_cold"
     },
     {
-        "duration": "67.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/http://www.instagram.com/archdigest_hot"
     },
     {
-        "duration": "49.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/http://www.instagram.com/archdigest_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://www.msn.com/ar-ae_cold"
     },
     {
-        "duration": "57.0",
+        "duration": "55.0",
         "name": "v8.runtime_stats.top_25/http://www.msn.com/ar-ae_hot"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "v8.runtime_stats.top_25/http://www.msn.com/ar-ae_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/http://www.pinterest.com/categories/popular_cold"
     },
     {
-        "duration": "58.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/http://www.pinterest.com/categories/popular_hot"
     },
     {
-        "duration": "55.0",
+        "duration": "52.0",
         "name": "v8.runtime_stats.top_25/http://www.pinterest.com/categories/popular_warm"
     },
     {
-        "duration": "51.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.qq.com_cold"
     },
     {
-        "duration": "59.0",
+        "duration": "58.0",
         "name": "v8.runtime_stats.top_25/http://www.qq.com_hot"
     },
     {
-        "duration": "56.0",
+        "duration": "55.0",
         "name": "v8.runtime_stats.top_25/http://www.qq.com_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/http://www.reddit.com_cold"
     },
     {
-        "duration": "53.0",
+        "duration": "63.0",
         "name": "v8.runtime_stats.top_25/http://www.reddit.com_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.reddit.com_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/http://www.twitter.com/taylorswift13_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/http://www.twitter.com/taylorswift13_hot"
     },
     {
-        "duration": "52.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/http://www.twitter.com/taylorswift13_warm"
     },
     {
@@ -4720,27 +4720,27 @@
         "name": "v8.runtime_stats.top_25/http://www.wikiwand.com/en/hill_hot"
     },
     {
-        "duration": "54.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/http://www.wikiwand.com/en/hill_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "46.0",
         "name": "v8.runtime_stats.top_25/http://www.yahoo.co.jp_cold"
     },
     {
-        "duration": "52.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/http://www.yahoo.co.jp_hot"
     },
     {
-        "duration": "50.0",
+        "duration": "49.0",
         "name": "v8.runtime_stats.top_25/http://www.yahoo.co.jp_warm"
     },
     {
-        "duration": "49.0",
+        "duration": "48.0",
         "name": "v8.runtime_stats.top_25/http://yandex.ru/search/?text=v8_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/http://yandex.ru/search/?text=v8_hot"
     },
     {
@@ -4748,39 +4748,39 @@
         "name": "v8.runtime_stats.top_25/http://yandex.ru/search/?text=v8_warm"
     },
     {
-        "duration": "54.0",
+        "duration": "53.0",
         "name": "v8.runtime_stats.top_25/https://adwords.google.com_cold"
     },
     {
-        "duration": "66.0",
+        "duration": "62.0",
         "name": "v8.runtime_stats.top_25/https://adwords.google.com_hot"
     },
     {
-        "duration": "64.0",
+        "duration": "60.0",
         "name": "v8.runtime_stats.top_25/https://adwords.google.com_warm"
     },
     {
-        "duration": "44.0",
+        "duration": "42.0",
         "name": "v8.runtime_stats.top_25/https://cdn.ampproject.org/c/www.bbc.co.uk/news/amp/37344292#log=3_cold"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/https://cdn.ampproject.org/c/www.bbc.co.uk/news/amp/37344292#log=3_hot"
     },
     {
-        "duration": "47.0",
+        "duration": "44.0",
         "name": "v8.runtime_stats.top_25/https://cdn.ampproject.org/c/www.bbc.co.uk/news/amp/37344292#log=3_warm"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "v8.runtime_stats.top_25/https://en.wikipedia.org/w/index.php?title=Barack_Obama&veaction=edit_cold"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "v8.runtime_stats.top_25/https://en.wikipedia.org/w/index.php?title=Barack_Obama&veaction=edit_hot"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "v8.runtime_stats.top_25/https://en.wikipedia.org/w/index.php?title=Barack_Obama&veaction=edit_warm"
     },
     {
@@ -4792,35 +4792,35 @@
         "name": "v8.runtime_stats.top_25/https://www.facebook.com/shakira_hot"
     },
     {
-        "duration": "51.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/https://www.facebook.com/shakira_warm"
     },
     {
-        "duration": "68.0",
+        "duration": "58.0",
         "name": "v8.runtime_stats.top_25/https://www.google.de/search?q=v8_cold"
     },
     {
-        "duration": "52.0",
+        "duration": "55.0",
         "name": "v8.runtime_stats.top_25/https://www.google.de/search?q=v8_hot"
     },
     {
-        "duration": "61.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/https://www.google.de/search?q=v8_warm"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "v8.runtime_stats.top_25/https://www.linkedin.com/m/_cold"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "v8.runtime_stats.top_25/https://www.linkedin.com/m/_hot"
     },
     {
-        "duration": "53.0",
+        "duration": "51.0",
         "name": "v8.runtime_stats.top_25/https://www.linkedin.com/m/_warm"
     },
     {
-        "duration": "46.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/https://www.youtube.com/watch?v=_kZsOISarzg_cold"
     },
     {
@@ -4828,19 +4828,19 @@
         "name": "v8.runtime_stats.top_25/https://www.youtube.com/watch?v=_kZsOISarzg_hot"
     },
     {
-        "duration": "49.0",
+        "duration": "59.0",
         "name": "v8.runtime_stats.top_25/https://www.youtube.com/watch?v=_kZsOISarzg_warm"
     },
     {
-        "duration": "47.0",
+        "duration": "50.0",
         "name": "v8.runtime_stats.top_25/https://www.youtube.com_cold"
     },
     {
-        "duration": "56.0",
+        "duration": "58.0",
         "name": "v8.runtime_stats.top_25/https://www.youtube.com_hot"
     },
     {
-        "duration": "52.0",
+        "duration": "56.0",
         "name": "v8.runtime_stats.top_25/https://www.youtube.com_warm"
     },
     {
@@ -4868,11 +4868,15 @@
         "name": "webrtc/hd_local_stream_10s"
     },
     {
-        "duration": "39.0",
+        "duration": "38.0",
         "name": "webrtc/insertable_streams_video_processing_camera_canvas2d_video"
     },
     {
-        "duration": "39.0",
+        "duration": "37.0",
+        "name": "webrtc/insertable_streams_video_processing_camera_noop_video"
+    },
+    {
+        "duration": "38.0",
         "name": "webrtc/insertable_streams_video_processing_camera_webgl_pc"
     },
     {
@@ -4880,7 +4884,7 @@
         "name": "webrtc/insertable_streams_video_processing_camera_webgl_video"
     },
     {
-        "duration": "39.0",
+        "duration": "38.0",
         "name": "webrtc/insertable_streams_video_processing_pc_webgl_video"
     },
     {
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
index 58a7af8d..02098d36 100644
--- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
@@ -24,7 +24,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 77,
+                "end": 56,
                 "abridged": false
             }
         }
@@ -32,7 +32,7 @@
     "2": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 77,
+                "begin": 56,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -42,7 +42,7 @@
                 "abridged": false
             },
             "blink_perf.parser": {
-                "end": 22,
+                "end": 29,
                 "abridged": false
             }
         }
@@ -50,7 +50,7 @@
     "3": {
         "benchmarks": {
             "blink_perf.parser": {
-                "begin": 22,
+                "begin": 29,
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
@@ -75,7 +75,7 @@
                 "abridged": false
             },
             "desktop_ui": {
-                "end": 1,
+                "end": 2,
                 "abridged": false
             }
         }
@@ -83,7 +83,7 @@
     "4": {
         "benchmarks": {
             "desktop_ui": {
-                "begin": 1,
+                "begin": 2,
                 "end": 14,
                 "abridged": false
             }
@@ -114,7 +114,7 @@
                 "abridged": false
             },
             "loading.desktop": {
-                "end": 7,
+                "end": 5,
                 "abridged": false
             }
         }
@@ -122,8 +122,8 @@
     "6": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 7,
-                "end": 23,
+                "begin": 5,
+                "end": 21,
                 "abridged": false
             }
         }
@@ -131,8 +131,8 @@
     "7": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 23,
-                "end": 37,
+                "begin": 21,
+                "end": 34,
                 "abridged": false
             }
         }
@@ -140,8 +140,8 @@
     "8": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 37,
-                "end": 48,
+                "begin": 34,
+                "end": 47,
                 "abridged": false
             }
         }
@@ -149,8 +149,8 @@
     "9": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 48,
-                "end": 58,
+                "begin": 47,
+                "end": 54,
                 "abridged": false
             }
         }
@@ -158,8 +158,8 @@
     "10": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 58,
-                "end": 71,
+                "begin": 54,
+                "end": 63,
                 "abridged": false
             }
         }
@@ -167,8 +167,8 @@
     "11": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 71,
-                "end": 84,
+                "begin": 63,
+                "end": 76,
                 "abridged": false
             }
         }
@@ -176,8 +176,8 @@
     "12": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 84,
-                "end": 97,
+                "begin": 76,
+                "end": 89,
                 "abridged": false
             }
         }
@@ -185,23 +185,23 @@
     "13": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 97,
-                "abridged": false
-            },
-            "media.desktop": {
-                "end": 15,
+                "begin": 89,
+                "end": 101,
                 "abridged": false
             }
         }
     },
     "14": {
         "benchmarks": {
+            "loading.desktop": {
+                "begin": 101,
+                "abridged": false
+            },
             "media.desktop": {
-                "begin": 15,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 8,
+                "end": 1,
                 "abridged": false
             }
         }
@@ -209,29 +209,24 @@
     "15": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 8,
+                "begin": 1,
                 "abridged": false
             },
             "octane": {
                 "abridged": false
-            },
-            "power.desktop": {
-                "abridged": false
-            },
-            "rasterize_and_record_micro.top_25": {
-                "end": 5,
-                "abridged": false
             }
         }
     },
     "16": {
         "benchmarks": {
+            "power.desktop": {
+                "abridged": false
+            },
             "rasterize_and_record_micro.top_25": {
-                "begin": 5,
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 20,
+                "end": 2,
                 "abridged": false
             }
         }
@@ -239,8 +234,8 @@
     "17": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 20,
-                "end": 48,
+                "begin": 2,
+                "end": 28,
                 "abridged": false
             }
         }
@@ -248,8 +243,8 @@
     "18": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 48,
-                "end": 74,
+                "begin": 28,
+                "end": 60,
                 "abridged": false
             }
         }
@@ -257,8 +252,8 @@
     "19": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 74,
-                "end": 104,
+                "begin": 60,
+                "end": 92,
                 "abridged": false
             }
         }
@@ -266,8 +261,8 @@
     "20": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 104,
-                "end": 136,
+                "begin": 92,
+                "end": 124,
                 "abridged": false
             }
         }
@@ -275,8 +270,8 @@
     "21": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 136,
-                "end": 168,
+                "begin": 124,
+                "end": 156,
                 "abridged": false
             }
         }
@@ -284,8 +279,8 @@
     "22": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 168,
-                "end": 198,
+                "begin": 156,
+                "end": 179,
                 "abridged": false
             }
         }
@@ -293,8 +288,8 @@
     "23": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 198,
-                "end": 227,
+                "begin": 179,
+                "end": 198,
                 "abridged": false
             }
         }
@@ -302,8 +297,8 @@
     "24": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 227,
-                "end": 253,
+                "begin": 198,
+                "end": 228,
                 "abridged": false
             }
         }
@@ -311,8 +306,8 @@
     "25": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 253,
-                "end": 282,
+                "begin": 228,
+                "end": 252,
                 "abridged": false
             }
         }
@@ -320,7 +315,16 @@
     "26": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 282,
+                "begin": 252,
+                "end": 279,
+                "abridged": false
+            }
+        }
+    },
+    "27": {
+        "benchmarks": {
+            "rendering.desktop": {
+                "begin": 279,
                 "abridged": false
             },
             "speedometer": {
@@ -331,7 +335,7 @@
             }
         }
     },
-    "27": {
+    "28": {
         "benchmarks": {
             "speedometer2": {
                 "abridged": false
@@ -343,16 +347,7 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 12,
-                "abridged": false
-            }
-        }
-    },
-    "28": {
-        "benchmarks": {
-            "system_health.common_desktop": {
-                "begin": 12,
-                "end": 43,
+                "end": 17,
                 "abridged": false
             }
         }
@@ -360,8 +355,8 @@
     "29": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 43,
-                "end": 71,
+                "begin": 17,
+                "end": 49,
                 "abridged": false
             }
         }
@@ -369,20 +364,20 @@
     "30": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 71,
-                "abridged": false
-            },
-            "system_health.memory_desktop": {
-                "end": 7,
+                "begin": 49,
+                "end": 74,
                 "abridged": false
             }
         }
     },
     "31": {
         "benchmarks": {
+            "system_health.common_desktop": {
+                "begin": 74,
+                "abridged": false
+            },
             "system_health.memory_desktop": {
-                "begin": 7,
-                "end": 19,
+                "end": 10,
                 "abridged": false
             }
         }
@@ -390,8 +385,8 @@
     "32": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 19,
-                "end": 33,
+                "begin": 10,
+                "end": 24,
                 "abridged": false
             }
         }
@@ -399,8 +394,8 @@
     "33": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 33,
-                "end": 51,
+                "begin": 24,
+                "end": 40,
                 "abridged": false
             }
         }
@@ -408,8 +403,8 @@
     "34": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 51,
-                "end": 67,
+                "begin": 40,
+                "end": 56,
                 "abridged": false
             }
         }
@@ -417,8 +412,8 @@
     "35": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 67,
-                "end": 76,
+                "begin": 56,
+                "end": 70,
                 "abridged": false
             }
         }
@@ -426,7 +421,16 @@
     "36": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 76,
+                "begin": 70,
+                "end": 79,
+                "abridged": false
+            }
+        }
+    },
+    "37": {
+        "benchmarks": {
+            "system_health.memory_desktop": {
+                "begin": 79,
                 "abridged": false
             },
             "tab_switching.typical_25": {
@@ -436,16 +440,7 @@
                 "abridged": false
             },
             "v8.browsing_desktop": {
-                "end": 4,
-                "abridged": false
-            }
-        }
-    },
-    "37": {
-        "benchmarks": {
-            "v8.browsing_desktop": {
-                "begin": 4,
-                "end": 21,
+                "end": 12,
                 "abridged": false
             }
         }
@@ -453,20 +448,20 @@
     "38": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 21,
-                "abridged": false
-            },
-            "v8.browsing_desktop-future": {
-                "end": 6,
+                "begin": 12,
+                "end": 26,
                 "abridged": false
             }
         }
     },
     "39": {
         "benchmarks": {
+            "v8.browsing_desktop": {
+                "begin": 26,
+                "abridged": false
+            },
             "v8.browsing_desktop-future": {
-                "begin": 6,
-                "end": 21,
+                "end": 19,
                 "abridged": false
             }
         }
@@ -474,11 +469,11 @@
     "40": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 21,
+                "begin": 19,
                 "abridged": false
             },
             "v8.runtime_stats.top_25": {
-                "end": 7,
+                "end": 3,
                 "abridged": false
             }
         }
@@ -486,8 +481,8 @@
     "41": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 7,
-                "end": 35,
+                "begin": 3,
+                "end": 32,
                 "abridged": false
             }
         }
@@ -495,8 +490,8 @@
     "42": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 35,
-                "end": 60,
+                "begin": 32,
+                "end": 59,
                 "abridged": false
             }
         }
@@ -504,8 +499,8 @@
     "43": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 60,
-                "end": 85,
+                "begin": 59,
+                "end": 84,
                 "abridged": false
             }
         }
@@ -513,8 +508,8 @@
     "44": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 85,
-                "end": 113,
+                "begin": 84,
+                "end": 114,
                 "abridged": false
             }
         }
@@ -522,7 +517,7 @@
     "45": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 113,
+                "begin": 114,
                 "abridged": false
             },
             "webrtc": {
@@ -532,55 +527,55 @@
     },
     "extra_infos": {
         "num_stories": 1225,
-        "predicted_min_shard_time": 1259.0,
-        "predicted_min_shard_index": 3,
-        "predicted_max_shard_time": 1557.0,
-        "predicted_max_shard_index": 34,
-        "shard #0": 1351.0,
-        "shard #1": 1356.0,
-        "shard #2": 1354.0,
-        "shard #3": 1259.0,
-        "shard #4": 1368.0,
-        "shard #5": 1378.0,
-        "shard #6": 1386.0,
-        "shard #7": 1392.0,
-        "shard #8": 1412.0,
-        "shard #9": 1342.0,
-        "shard #10": 1336.0,
-        "shard #11": 1394.0,
-        "shard #12": 1314.0,
-        "shard #13": 1351.0,
-        "shard #14": 1420.0,
-        "shard #15": 1348.0,
-        "shard #16": 1358.0,
-        "shard #17": 1375.0,
-        "shard #18": 1334.0,
-        "shard #19": 1346.0,
-        "shard #20": 1368.0,
-        "shard #21": 1359.0,
-        "shard #22": 1343.0,
-        "shard #23": 1377.0,
-        "shard #24": 1341.0,
-        "shard #25": 1350.0,
-        "shard #26": 1321.0,
-        "shard #27": 1363.0,
-        "shard #28": 1371.0,
-        "shard #29": 1339.0,
-        "shard #30": 1339.0,
-        "shard #31": 1374.0,
-        "shard #32": 1389.0,
-        "shard #33": 1389.0,
-        "shard #34": 1557.0,
-        "shard #35": 1392.0,
-        "shard #36": 1302.0,
-        "shard #37": 1312.0,
-        "shard #38": 1375.0,
-        "shard #39": 1389.0,
-        "shard #40": 1319.0,
-        "shard #41": 1324.0,
-        "shard #42": 1312.0,
-        "shard #43": 1329.0,
-        "shard #44": 1336.0,
-        "shard #45": 1339.0
+        "predicted_min_shard_time": 1236.0,
+        "predicted_min_shard_index": 36,
+        "predicted_max_shard_time": 1467.0,
+        "predicted_max_shard_index": 35,
+        "shard #0": 1349.0,
+        "shard #1": 1355.0,
+        "shard #2": 1357.0,
+        "shard #3": 1350.0,
+        "shard #4": 1343.0,
+        "shard #5": 1367.0,
+        "shard #6": 1326.0,
+        "shard #7": 1372.0,
+        "shard #8": 1386.0,
+        "shard #9": 1270.0,
+        "shard #10": 1316.0,
+        "shard #11": 1330.0,
+        "shard #12": 1346.0,
+        "shard #13": 1344.0,
+        "shard #14": 1408.0,
+        "shard #15": 1363.0,
+        "shard #16": 1373.0,
+        "shard #17": 1351.0,
+        "shard #18": 1372.0,
+        "shard #19": 1340.0,
+        "shard #20": 1356.0,
+        "shard #21": 1360.0,
+        "shard #22": 1381.0,
+        "shard #23": 1338.0,
+        "shard #24": 1335.0,
+        "shard #25": 1368.0,
+        "shard #26": 1351.0,
+        "shard #27": 1308.0,
+        "shard #28": 1344.0,
+        "shard #29": 1363.0,
+        "shard #30": 1348.0,
+        "shard #31": 1451.0,
+        "shard #32": 1449.0,
+        "shard #33": 1317.0,
+        "shard #34": 1335.0,
+        "shard #35": 1467.0,
+        "shard #36": 1236.0,
+        "shard #37": 1380.0,
+        "shard #38": 1328.0,
+        "shard #39": 1307.0,
+        "shard #40": 1342.0,
+        "shard #41": 1374.0,
+        "shard #42": 1382.0,
+        "shard #43": 1336.0,
+        "shard #44": 1366.0,
+        "shard #45": 1350.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 636f21c88..9c04e369 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -200,6 +200,7 @@
 crbug.com/1017244 [ desktop ] rendering.desktop/youtube_2018 [ Skip ]
 crbug.com/1017244 [ desktop ] rendering.desktop/youtube_pinch_2018 [ Skip ]
 crbug.com/1044962 [ win ] rendering.desktop/camera_to_webgl [ Skip ]
+crbug.com/1176809 [ win ] rendering.desktop/gmail_move_2018 [ Skip ]
 
 # Benchmark: rendering.mobile
 crbug.com/785485 [ android-webview ] rendering.mobile/kevs_3d [ Skip ]
diff --git a/tools/perf/page_sets/update_webrtc_cases b/tools/perf/page_sets/update_webrtc_cases
index 26b3434e..9321632f 100755
--- a/tools/perf/page_sets/update_webrtc_cases
+++ b/tools/perf/page_sets/update_webrtc_cases
@@ -32,8 +32,9 @@
             'src/content/datachannel/datatransfer',
             'src/content/getusermedia/resolution',
             'src/content/insertable-streams/video-processing',
-        ],
-        'revision': '583fc1a6efacd19d9485be381a8698d12748b671',
+            'src/content/peerconnection/negotiate-timing',
+            ],
+        'revision': '4b198b679350f836809b40516c25d4b17eb22e9d',
     },
 }
 
diff --git a/tools/perf/page_sets/webrtc_cases.py b/tools/perf/page_sets/webrtc_cases.py
index efa7527..f7513b5 100644
--- a/tools/perf/page_sets/webrtc_cases.py
+++ b/tools/perf/page_sets/webrtc_cases.py
@@ -188,6 +188,71 @@
         description='Number of frames received at the sink video.')
 
 
+class NegotiateTiming(WebrtcPage):
+  """Why: Measure how long renegotiation takes with large SDP blobs."""
+
+  def __init__(self, page_set, tags):
+    super(NegotiateTiming,
+          self).__init__(url='file://webrtc_cases/negotiate-timing.html',
+                         name='negotiate-timing',
+                         page_set=page_set,
+                         tags=tags)
+
+  def ExecuteTest(self, action_runner):
+    action_runner.ExecuteJavaScript('start()')
+    action_runner.WaitForJavaScriptCondition('!callButton.disabled')
+    action_runner.ExecuteJavaScript('call()')
+    action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
+    # Due to suspicion of renegotiate activating too early:
+    action_runner.Wait(1)
+    # Negotiate 50 transceivers, then negotiate back to 1, simulating Meet "pin"
+    action_runner.ExecuteJavaScript('videoSectionsField.value = 50')
+    action_runner.ExecuteJavaScript('renegotiate()')
+    action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
+    action_runner.ExecuteJavaScript('videoSectionsField.value = 1')
+    action_runner.ExecuteJavaScript('renegotiate()')
+    action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
+    # Negotiate back up to 50, simulating Meet "unpin". This is what gets measured.
+    action_runner.ExecuteJavaScript('videoSectionsField.value = 50')
+    action_runner.ExecuteJavaScript('renegotiate()')
+    action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
+    result = action_runner.EvaluateJavaScript('result')
+
+    self.AddMeasurement('callerSetLocalDescription',
+                        'ms',
+                        result['callerSetLocalDescription'],
+                        description='Time for caller SetLocalDescription')
+    self.AddMeasurement('calleeSetLocalDescription',
+                        'ms',
+                        result['calleeSetLocalDescription'],
+                        description='Time for callee SetLocalDescription')
+    self.AddMeasurement('callerSetRemoteDescription',
+                        'ms',
+                        result['callerSetRemoteDescription'],
+                        description='Time for caller SetRemoteDescription')
+    self.AddMeasurement('calleeSetRemoteDescription',
+                        'ms',
+                        result['calleeSetRemoteDescription'],
+                        description='Time for callee SetRemoteDescription')
+    self.AddMeasurement('callerCreateOffer',
+                        'ms',
+                        result['callerCreateOffer'],
+                        description='Time for overall offer/answer handshake')
+    self.AddMeasurement('calleeCreateAnswer',
+                        'ms',
+                        result['calleeCreateAnswer'],
+                        description='Time for overall offer/answer handshake')
+    self.AddMeasurement('elapsedTime',
+                        'ms',
+                        result['elapsedTime'],
+                        description='Time for overall offer/answer handshake')
+    self.AddMeasurement(
+        'audioImpairment',
+        'count',
+        result['audioImpairment'],
+        description='Number of late audio samples concealed during negotiation')
+
+
 class WebrtcPageSet(story.StorySet):
   def __init__(self):
     super(WebrtcPageSet, self).__init__(
@@ -237,3 +302,4 @@
                                          'webgl',
                                          'pc',
                                          tags=['insertableStreams']))
+    self.AddStory(NegotiateTiming(self, tags=['sdp']))
diff --git a/tools/perf/page_sets/webrtc_cases/datatransfer.js b/tools/perf/page_sets/webrtc_cases/datatransfer.js
index a1bb3fe..5bcbd9c4 100644
--- a/tools/perf/page_sets/webrtc_cases/datatransfer.js
+++ b/tools/perf/page_sets/webrtc_cases/datatransfer.js
@@ -195,7 +195,7 @@
 function onReceiveMessageCallback(event) {
   receiveProgress.value += event.data.length;
   currentThroughput = receiveProgress.value / (performance.now() - sendStartTime);
-  console.log('Current Throughput is:', currentThroughput, 'bytes/sec')
+  console.log('Current Throughput is:', currentThroughput, 'bytes/sec');
 
   // Workaround for a bug in Chrome which prevents the closing event from being raised by the
   // remote side. Also a workaround for Firefox which does not send all pending data when closing
diff --git a/tools/perf/page_sets/webrtc_cases/negotiate-timing.html b/tools/perf/page_sets/webrtc_cases/negotiate-timing.html
new file mode 100644
index 0000000..60c6ef0
--- /dev/null
+++ b/tools/perf/page_sets/webrtc_cases/negotiate-timing.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+-->
+<html>
+<head>
+
+
+    <base target="_blank">
+
+    <title>Peer connection - Renegotiate</title>
+
+
+</head>
+
+<body>
+
+<div id="container">
+    <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
+        <span>Peer connection negotiation timing</span></h1>
+
+    <video id="localVideo" playsinline autoplay muted></video>
+    <video id="remoteVideo" playsinline autoplay></video>
+
+    <div>
+        <button id="startButton">Start</button>
+        <button id="callButton">Call</button>
+        <button id="renegotiateButton">Renegotiate</button>
+        <button id="hangupButton">Hang Up</button>
+    </div>
+    <div>
+      <p>
+        Video sections after renegotiating: <input type="number" id="videoSections" value="5">
+      </p>
+    </div>
+    <p>View the console to see logging. The <code>MediaStream</code> object <code>localStream</code>, and the <code>RTCPeerConnection</code>
+        objects <code>pc1</code> and <code>pc2</code> are in global scope, so you can inspect them in the console as
+        well.</p>
+    <p>
+      <div id="log">
+        Log goes here
+      </div>
+    </p>
+    <a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/peerconnection/negotiate-timing"
+       title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
+
+</div>
+
+
+<script src="negotiate-timing.js"></script>
+</body></html>
\ No newline at end of file
diff --git a/tools/perf/page_sets/webrtc_cases/negotiate-timing.js b/tools/perf/page_sets/webrtc_cases/negotiate-timing.js
new file mode 100644
index 0000000..b86c9754
--- /dev/null
+++ b/tools/perf/page_sets/webrtc_cases/negotiate-timing.js
@@ -0,0 +1,242 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+
+'use strict';
+
+const startButton = document.getElementById('startButton');
+const callButton = document.getElementById('callButton');
+const renegotiateButton = document.getElementById('renegotiateButton');
+const hangupButton = document.getElementById('hangupButton');
+const log = document.getElementById('log');
+const videoSectionsField = document.getElementById('videoSections');
+
+callButton.disabled = true;
+hangupButton.disabled = true;
+renegotiateButton.disabled = true;
+startButton.onclick = start;
+callButton.onclick = call;
+renegotiateButton.onclick = renegotiate;
+hangupButton.onclick = hangup;
+
+let startTime;
+const localVideo = document.getElementById('localVideo');
+const remoteVideo = document.getElementById('remoteVideo');
+
+let audioTransceiver;
+let audioImpairmentAtStart = 0;
+
+let result;
+
+localVideo.addEventListener('loadedmetadata', function() {
+  console.log(`Local video videoWidth: ${this.videoWidth}px,  videoHeight: ${this.videoHeight}px`);
+});
+
+remoteVideo.addEventListener('loadedmetadata', function() {
+  console.log(`Remote video videoWidth: ${this.videoWidth}px,  videoHeight: ${this.videoHeight}px`);
+});
+
+remoteVideo.onresize = () => {
+  console.log(`Remote video size changed to ${remoteVideo.videoWidth}x${remoteVideo.videoHeight}`);
+  console.warn('RESIZE', remoteVideo.videoWidth, remoteVideo.videoHeight);
+  // We'll use the first onsize callback as an indication that video has started
+  // playing out.
+  if (startTime) {
+    const elapsedTime = window.performance.now() - startTime;
+    console.log(`Setup time: ${elapsedTime.toFixed(3)}ms`);
+    startTime = null;
+  }
+};
+
+let localStream;
+let pc1;
+let pc2;
+
+function logToScreen(text) {
+  log.append(document.createElement('br'));
+  log.append(text);
+}
+
+function getName(pc) {
+  return (pc === pc1) ? 'pc1' : 'pc2';
+}
+
+function getOtherPc(pc) {
+  return (pc === pc1) ? pc2 : pc1;
+}
+
+async function start() {
+  console.log('Requesting local stream');
+  startButton.disabled = true;
+  const stream = await navigator.mediaDevices
+      .getUserMedia({
+        audio: true,
+        video: true
+      });
+  console.log('Received local stream');
+  localVideo.srcObject = stream;
+  localStream = stream;
+  callButton.disabled = false;
+}
+
+async function runOfferAnswer() {
+  const startTime = performance.now();
+  const result = {};
+  const offer = await pc1.createOffer();
+  const markTime1 = performance.now();
+  result.callerCreateOffer = markTime1 - startTime;
+  await pc1.setLocalDescription(offer);
+  const markTime2 = performance.now();
+  result.callerSetLocalDescription = markTime2 - markTime1;
+  await pc2.setRemoteDescription(offer);
+  const markTime3 = performance.now();
+  result.calleeSetRemoteDescription = markTime3 - markTime2;
+  const answer = await pc2.createAnswer();
+  const markTime4 = performance.now();
+  result.calleeCreateAnswer = markTime4 - markTime3;
+  await pc1.setRemoteDescription(answer);
+  const markTime5 = performance.now();
+  result.callerSetRemoteDescription = markTime5 - markTime4;
+  await pc2.setLocalDescription(answer);
+  const markTime6 = performance.now();
+  result.calleeSetLocalDescription = markTime6 - markTime5;
+  result.elapsedTime = markTime6 - startTime;
+  return result;
+}
+
+async function call() {
+  callButton.disabled = true;
+  renegotiateButton.disabled = false;
+  hangupButton.disabled = false;
+  console.log('Starting call');
+  startTime = window.performance.now();
+  const audioTracks = localStream.getAudioTracks();
+  if (audioTracks.length > 0) {
+    console.log(`Using audio device: ${audioTracks[0].label}`);
+  }
+  const servers = null;
+  pc1 = new RTCPeerConnection(servers);
+  console.log('Created local peer connection object pc1');
+  pc1.onicecandidate = e => onIceCandidate(pc1, e);
+  pc2 = new RTCPeerConnection(servers);
+  console.log('Created remote peer connection object pc2');
+  pc2.onicecandidate = e => onIceCandidate(pc2, e);
+  pc1.oniceconnectionstatechange = e => onIceStateChange(pc1, e);
+  pc2.oniceconnectionstatechange = e => onIceStateChange(pc2, e);
+  pc2.addEventListener('track', gotRemoteStream, {once: true});
+
+  localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
+  console.log('Added local stream to pc1');
+
+  await runOfferAnswer();
+  console.log('Initial negotiation complete');
+}
+
+function gotRemoteStream(e) {
+  console.log('gotRemoteStream', e.track, e.streams[0]);
+  if (e.streams[0]) {
+    // reset srcObject to work around minor bugs in Chrome and Edge.
+    remoteVideo.srcObject = null;
+    remoteVideo.srcObject = e.streams[0];
+  }
+}
+
+async function onIceCandidate(pc, event) {
+  if (event.candidate) {
+    console.log(`${getName(pc)} emitted ICE candidate for index ${event.candidate.sdpMLineIndex}:\n${event.candidate.candidate}`);
+  } else {
+    console.log(`$getName(pc)} ICE NULL candidate`);
+  }
+  await getOtherPc(pc).addIceCandidate(event.candidate);
+  console.log(`${getName(pc)} addIceCandidate success`);
+}
+
+function onIceStateChange(pc, event) {
+  if (pc) {
+    console.log(`${getName(pc)} ICE state: ${pc.iceConnectionState}`);
+    console.log('ICE state change event, state: ', pc.iceConnectionState);
+  }
+}
+
+function adjustTransceiverCounts(pc, videoCount) {
+  const currentVideoTransceivers = pc.getTransceivers().filter(tr => tr.receiver.track.kind == 'video');
+  const currentVideoCount = currentVideoTransceivers.length;
+  if (currentVideoCount < videoCount) {
+    console.log('Adding ' + (videoCount - currentVideoCount) + ' transceivers');
+    for (let i = currentVideoCount; i < videoCount; ++i) {
+      pc.addTransceiver('video');
+    }
+  } else if (currentVideoCount > videoCount) {
+    console.log('Stopping ' + (currentVideoCount - videoCount) + ' transceivers');
+    for (let i = videoCount; i < currentVideoCount; ++i) {
+      currentVideoTransceivers[i].stop();
+    }
+  } else {
+    console.log(`No adjustment, video count is ${currentVideoCount}, target was ${videoCount}`);
+  }
+}
+
+async function getAudioImpairment(audioTransceiver) {
+  const stats = await audioTransceiver.receiver.getStats();
+  let currentImpairment;
+  stats.forEach(stat => {
+    if (stat.type == 'track') {
+      currentImpairment = stat.concealedSamples;
+    }
+  });
+  console.log('Found impairment value ', currentImpairment);
+  return currentImpairment;
+}
+
+async function baselineAudioImpairment(pc) {
+  audioTransceiver = pc.getTransceivers().find(tr => tr.receiver.track.kind == 'audio');
+  console.log('Found audio transceiver');
+  audioImpairmentAtStart = await getAudioImpairment(audioTransceiver);
+}
+
+async function measureAudioImpairment(pc) {
+  const startTime = performance.now();
+  const audioImpairmentNow = await getAudioImpairment(audioTransceiver);
+  console.log('Measurement took ' + (performance.now() - startTime) + ' msec');
+  return audioImpairmentNow - audioImpairmentAtStart;
+}
+
+
+async function renegotiate() {
+  renegotiateButton.disabled = true;
+  adjustTransceiverCounts(pc1, parseInt(videoSectionsField.value));
+  await baselineAudioImpairment(pc2);
+  const previousVideoTransceiverCount = pc2.getTransceivers().filter(tr => tr.receiver.track.kind == 'video').length;
+  result = await runOfferAnswer();
+  console.log(`Renegotiate finished after ${result.elapsedTime} milliseconds`);
+  const currentVideoTransceiverCount = pc2.getTransceivers().filter(tr => tr.receiver.track.kind == 'video').length;
+  result.audioImpairment = await measureAudioImpairment(pc2);
+  logToScreen(`Negotiation from ${previousVideoTransceiverCount} to ${currentVideoTransceiverCount} video transceivers took ${result.elapsedTime.toFixed(2)} milliseconds, audio impairment ${result.audioImpairment}`);
+  console.log('Results: ', JSON.stringify(result, ' ', 2));
+  renegotiateButton.disabled = false;
+}
+
+function hangup() {
+  console.log('Ending call');
+  pc1.close();
+  pc2.close();
+  pc1 = null;
+  pc2 = null;
+
+  console.log('Releasing camera');
+  const videoTracks = localStream.getVideoTracks();
+  videoTracks.forEach(videoTrack => {
+    videoTrack.stop();
+    localStream.removeTrack(videoTrack);
+  });
+  localVideo.srcObject = null;
+
+  hangupButton.disabled = true;
+  callButton.disabled = true;
+  renegotiateButton.disabled = true;
+  startButton.disabled = false;
+}
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 69d8c2b..518f8073 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -166,7 +166,7 @@
  <item id="interest_feedv2_send" added_in_milestone="83" hash_code="85742023" type="0" content_hash_code="49706671" os_list="linux,windows" file_path="components/feed/core/v2/feed_network_impl.cc"/>
  <item id="intranet_redirect_detector" added_in_milestone="62" hash_code="21785164" type="0" content_hash_code="62025595" os_list="linux,windows" file_path="chrome/browser/intranet_redirect_detector.cc"/>
  <item id="invalidation_service" added_in_milestone="62" hash_code="72354423" type="0" deprecated="2020-01-23" content_hash_code="78425687" file_path=""/>
- <item id="javascript_report_error" added_in_milestone="87" hash_code="109607776" type="0" content_hash_code="7229012" os_list="linux" file_path="chrome/browser/error_reporting/chrome_js_error_report_processor.cc"/>
+ <item id="javascript_report_error" added_in_milestone="87" hash_code="109607776" type="0" content_hash_code="7229012" os_list="linux" file_path="chrome/browser/error_reporting/chrome_js_error_report_processor_nonchromeos.cc"/>
  <item id="kids_chrome_management_client_classify_url" added_in_milestone="77" hash_code="109987793" type="0" deprecated="2019-07-30" content_hash_code="112740597" file_path=""/>
  <item id="lib_address_input" added_in_milestone="62" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
  <item id="litepages_robots_rules" added_in_milestone="89" hash_code="50910588" type="0" content_hash_code="72567080" os_list="linux,windows" file_path="chrome/browser/subresource_redirect/origin_robots_rules.cc"/>
@@ -357,6 +357,7 @@
  <item id="user_info_fetcher" added_in_milestone="62" hash_code="22265491" type="0" content_hash_code="72016232" os_list="linux,windows" file_path="components/policy/core/common/cloud/user_info_fetcher.cc"/>
  <item id="video_tutorial_fetcher" added_in_milestone="87" hash_code="69879956" type="0" content_hash_code="121911479" os_list="linux,windows" file_path="chrome/browser/video_tutorials/internal/tutorial_fetcher.cc"/>
  <item id="viz_devtools_server" added_in_milestone="72" hash_code="16292315" type="0" content_hash_code="70061664" os_list="linux,windows" file_path="components/ui_devtools/devtools_server.cc"/>
+ <item id="web_app_origin_association_download" added_in_milestone="90" hash_code="128608592" type="0" content_hash_code="55648317" os_list="linux,windows" file_path="components/webapps/services/web_app_origin_association/web_app_origin_association_fetcher.cc"/>
  <item id="web_bundle_loader" added_in_milestone="84" hash_code="114615359" type="0" content_hash_code="57390734" os_list="linux,windows" file_path="content/browser/web_package/web_bundle_utils.cc"/>
  <item id="web_history_counter" added_in_milestone="62" hash_code="137457845" type="1" second_id="110307337" content_hash_code="49663381" os_list="linux,windows" semantics_fields="2,3,4" policy_fields="4" file_path="components/browsing_data/core/counters/history_counter.cc"/>
  <item id="web_history_delete_url" added_in_milestone="74" hash_code="41749213" type="1" second_id="110307337" content_hash_code="25943026" os_list="linux,windows" semantics_fields="2,3,4" policy_fields="4" file_path="components/history/core/browser/history_service.cc"/>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 2788aa9..daab9de 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -161,6 +161,7 @@
       <traffic_annotation unique_id="webid"/>
       <traffic_annotation unique_id="managed_configuration_loader"/>
       <traffic_annotation unique_id="box_access_token_fetcher"/>
+      <traffic_annotation unique_id="web_app_origin_association_download"/>
     </sender>
   </group>
   <group name="Admin Features">
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 8a860687..62033b4f 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -319,7 +319,6 @@
     "java/src/org/chromium/ui/resources/HandleViewResources.java",
     "java/src/org/chromium/ui/resources/LayoutResource.java",
     "java/src/org/chromium/ui/resources/Resource.java",
-    "java/src/org/chromium/ui/resources/ResourceExtractor.java",
     "java/src/org/chromium/ui/resources/ResourceFactory.java",
     "java/src/org/chromium/ui/resources/ResourceLoader.java",
     "java/src/org/chromium/ui/resources/ResourceManager.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java b/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java
index 14be47ed..1f166ef 100644
--- a/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java
+++ b/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java
@@ -7,7 +7,6 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 
-import org.chromium.base.BundleUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.LocaleUtils;
 import org.chromium.base.Log;
@@ -21,8 +20,7 @@
  * This class provides the resource bundle related methods for the native
  * library.
  *
- * IMPORTANT: Clients that use {@link ResourceBundle} and/or
- * {@link org.chromium.ui.resources.ResourceExtractor} MUST call either
+ * IMPORTANT: Clients that use {@link ResourceBundle} MUST call either
  * {@link ResourceBundle#setAvailablePakLocales(String[], String[])} or
  * {@link ResourceBundle#setNoAvailableLocalePaks()} before calling the getters in this class.
  */
@@ -65,17 +63,13 @@
     }
 
     /**
-     * Return the list of available locales. For bundle builds this is the uncompressed locales list
-     * and for apk builds this is the compressed locales list.
+     * Return the list of available locales.
      * @return The correct locale list for this build.
      */
     public static String[] getAvailableLocales() {
         assert sCompressedLocales != null;
         assert sUncompressedLocales != null;
-        if (BundleUtils.isBundle()) {
-            return sUncompressedLocales;
-        }
-        return sCompressedLocales;
+        return sUncompressedLocales;
     }
 
     /**
@@ -120,6 +114,13 @@
         try (AssetFileDescriptor afd = manager.openNonAssetFd(assetPath)) {
             return assetPath;
         } catch (IOException e) {
+            // Fallback for apk targets.
+            // TODO(crbug.com/1176290): Remove the need for this fallback logic.
+            String fallbackPath = "assets/locales/" + locale + ".pak";
+            try (AssetFileDescriptor afd = manager.openNonAssetFd(fallbackPath)) {
+                return fallbackPath;
+            } catch (IOException e2) {
+            }
             if (logError) {
                 Log.e(TAG, "path=%s", assetPath, e);
             }
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
deleted file mode 100644
index ad6b0d7f..0000000
--- a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 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.
-
-package org.chromium.ui.resources;
-
-import android.content.res.AssetManager;
-
-import org.chromium.base.BuildInfo;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.FileUtils;
-import org.chromium.base.LocaleUtils;
-import org.chromium.base.Log;
-import org.chromium.base.PathUtils;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.TraceEvent;
-import org.chromium.base.task.PostTask;
-import org.chromium.base.task.TaskTraits;
-import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.base.ResourceBundle;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Handles extracting the necessary resources bundled in an APK and moving them to a location on
- * the file system accessible from the native code.
- */
-public class ResourceExtractor {
-    private static final String TAG = "ui";
-    private static final String ICU_DATA_FILENAME = "icudtl.dat";
-    private static final String V8_SNAPSHOT_DATA_FILENAME = "snapshot_blob.bin";
-    private static final String FALLBACK_LOCALE = "en-US";
-    private static final String COMPRESSED_LOCALES_DIR = "locales";
-
-    private class ExtractTask implements Runnable {
-        private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
-        private final String mUiLanguage;
-        private final CountDownLatch mLatch = new CountDownLatch(1);
-        private boolean mDone;
-
-        public ExtractTask(String uiLanguage) {
-            mUiLanguage = uiLanguage;
-        }
-
-        @Override
-        public void run() {
-            try (TraceEvent e = TraceEvent.scoped("ResourceExtractor.ExtractTask.doInBackground")) {
-                doInBackgroundImpl();
-            }
-            synchronized (this) {
-                mDone = true;
-            }
-            mLatch.countDown();
-
-            PostTask.postTask(mResultTaskTraits, () -> {
-                try (TraceEvent e =
-                                TraceEvent.scoped("ResourceExtractor.ExtractTask.onPostExecute")) {
-                    onPostExecuteImpl();
-                }
-            });
-        }
-
-        private void doInBackgroundImpl() {
-            final File outputDir = getOutputDir();
-            String[] assetPaths = detectFilesToExtract(mUiLanguage);
-
-            // Use a suffix for extracted files in order to guarantee that the version of the file
-            // on disk matches up with the version of the APK.
-            String extractSuffix = BuildInfo.getInstance().extractedFileSuffix;
-            String[] outputNames = new String[assetPaths.length];
-            for (int n = 0; n < assetPaths.length; ++n) {
-                String assetPath = assetPaths[n];
-                outputNames[n] =
-                        assetPath.substring(assetPath.lastIndexOf('/') + 1) + extractSuffix;
-            }
-
-            String[] existingFileNames = outputDir.list();
-            boolean allFilesExist = existingFileNames != null;
-            if (allFilesExist) {
-                List<String> existingFiles = Arrays.asList(existingFileNames);
-                for (String outputName : outputNames) {
-                    allFilesExist &= existingFiles.contains(outputName);
-                }
-            }
-            // This is the normal case.
-            if (allFilesExist) {
-                return;
-            }
-            // A missing file means Chrome has updated. Delete stale files first.
-            deleteFiles(existingFileNames);
-
-            outputDir.mkdirs();
-            if (!outputDir.exists()) {
-                // Return value of mkdirs() sometimes incorrect? https://crbug.com/849550
-                throw new RuntimeException();
-            }
-
-            for (int n = 0; n < assetPaths.length; ++n) {
-                String assetPath = assetPaths[n];
-                File output = new File(outputDir, outputNames[n]);
-                if (!FileUtils.extractAsset(
-                            ContextUtils.getApplicationContext(), assetPath, output)) {
-                    // The app would just crash later if files are missing.
-                    throw new RuntimeException();
-                }
-            }
-        }
-
-        private void onPostExecuteImpl() {
-            ThreadUtils.assertOnUiThread();
-            for (int i = 0; i < mCompletionCallbacks.size(); i++) {
-                mCompletionCallbacks.get(i).run();
-            }
-            mCompletionCallbacks.clear();
-        }
-
-        public void await() throws Exception {
-            mLatch.await();
-        }
-
-        public synchronized boolean isDone() {
-            return mDone;
-        }
-    }
-
-    private ExtractTask mExtractTask;
-    private TaskTraits mResultTaskTraits;
-
-    private static ResourceExtractor sInstance;
-
-    public static ResourceExtractor get() {
-        if (sInstance == null) {
-            sInstance = new ResourceExtractor();
-        }
-        return sInstance;
-    }
-
-    private static String[] detectFilesToExtract(String uiLanguage) {
-        Locale defaultLocale = Locale.getDefault();
-        String androidLanguage = defaultLocale.getLanguage();
-        String chromiumLanguage = LocaleUtils.getUpdatedLanguageForChromium(androidLanguage);
-
-        // NOTE: The UI language will differ from the application's language
-        // when the system locale is not directly supported by Chrome's
-        // resources.
-        Log.i(TAG, "Using UI locale %s, system locale: %s (Android name: %s)", uiLanguage,
-                chromiumLanguage, androidLanguage);
-
-        // Currenty (Apr 2018), this array can be as big as 6 entries, so using a capacity
-        // that allows a bit of growth, but is still in the right ballpark..
-        ArrayList<String> activeLocales = new ArrayList<String>(6);
-        String[] compressedLocales = ResourceBundle.getAvailableCompressedPakLocales();
-        for (String locale : compressedLocales) {
-            if (LocalizationUtils.chromiumLocaleMatchesLanguage(locale, uiLanguage)) {
-                activeLocales.add(locale);
-            }
-        }
-        if (activeLocales.isEmpty()) {
-            assert compressedLocales.length > 0;
-            assert Arrays.asList(compressedLocales).contains(FALLBACK_LOCALE);
-            activeLocales.add(FALLBACK_LOCALE);
-        }
-
-        // * For bundles, locale pak files are always stored uncompressed
-        //   either under base.apk!/assets/fallback-locales/<locale>.pak or
-        //   base-<lang>.apk!/assets/locales#lang_<lang>/<locale>.pak. They
-        //   never need to be extracted.
-        //
-        // * For regular APKs, the locale pak files are stored under:
-        //      base.apk!/assets/locales/<locale>.pak
-        //
-        //   where <locale> is a Chromium-specific locale name.
-        //
-        AssetManager assetManager = ContextUtils.getApplicationAssets();
-        if (!assetPathHasFile(
-                    assetManager, COMPRESSED_LOCALES_DIR, activeLocales.get(0) + ".pak")) {
-            Log.i(TAG, "No locale pak files to extract, assuming app bundle.");
-            return new String[] {};
-        }
-
-        // This is a regular APK, and all pak files are available.
-        // Return the list of locale pak file paths corresponding to the current language.
-        String[] localePakFiles = new String[activeLocales.size()];
-        for (int n = 0; n < activeLocales.size(); ++n) {
-            localePakFiles[n] = COMPRESSED_LOCALES_DIR + '/' + activeLocales.get(n) + ".pak";
-        }
-        Log.i(TAG, "UI Language: %s requires .pak files: %s", uiLanguage,
-                Arrays.toString(activeLocales.toArray()));
-
-        return localePakFiles;
-    }
-
-    /**
-     * Check that an AssetManager instance has a specific asset file.
-     *
-     * @param assetManager The application's AssetManager instance.
-     * @param assetPath Asset directory path (e.g. "assets/locales").
-     * @param assetFile Asset file name inside assetPath.
-     * @return true iff the asset file is available.
-     */
-    private static boolean assetPathHasFile(
-            AssetManager assetManager, String assetPath, String assetFile) {
-        String assetFilePath = assetPath + '/' + assetFile;
-        try {
-            InputStream input = assetManager.open(assetFilePath);
-            input.close();
-            Log.i(TAG, "Found asset file: " + assetFilePath);
-            return true;
-        } catch (IOException e) {
-            Log.i(TAG, "Missing asset file: " + assetFilePath);
-            return false;
-        }
-    }
-
-    /**
-     * Synchronously wait for the resource extraction to be completed.
-     * <p>
-     * This method is bad and you should feel bad for using it.
-     *
-     * @see #addCompletionCallback(Runnable)
-     */
-    public void waitForCompletion() {
-        if (mExtractTask == null || shouldSkipPakExtraction()) {
-            return;
-        }
-
-        try {
-            mExtractTask.await();
-        } catch (Exception e) {
-            assert false;
-        }
-    }
-
-    /**
-     * Sets the traits to use for the reply task.
-     */
-    public void setResultTraits(TaskTraits traits) {
-        mResultTaskTraits = traits;
-    }
-
-    /**
-     * Adds a callback to be notified upon the completion of resource extraction.
-     * <p>
-     * If the resource task has already completed, the callback will be posted to the UI message
-     * queue.  Otherwise, it will be executed after all the resources have been extracted.
-     * <p>
-     * This must be called on the UI thread.  The callback will also always be executed on
-     * the UI thread.
-     *
-     * @param callback The callback to be enqueued.
-     */
-    public void addCompletionCallback(Runnable callback) {
-        ThreadUtils.assertOnUiThread();
-
-        if (shouldSkipPakExtraction()) {
-            PostTask.postTask(mResultTaskTraits, callback);
-            return;
-        }
-
-        assert mExtractTask != null;
-        if (mExtractTask.isDone()) {
-            PostTask.postTask(mResultTaskTraits, callback);
-        } else {
-            mExtractTask.mCompletionCallbacks.add(callback);
-        }
-    }
-
-    /**
-     * This will extract the application pak resources in an
-     * AsyncTask. Call waitForCompletion() at the point resources
-     * are needed to block until the task completes.
-     *
-     * @param uiLanguage The language to extract.
-     */
-    public void startExtractingResources(String uiLanguage) {
-        if (mExtractTask != null) {
-            return;
-        }
-
-        // If a previous release extracted resources, and the current release does not, delete the
-        // old files since they are no longer needed.
-        if (shouldSkipPakExtraction()) {
-            PostTask.postTask(
-                    TaskTraits.BEST_EFFORT, () -> { deleteFiles(getOutputDir().list()); });
-            return;
-        }
-
-        mExtractTask = new ExtractTask(uiLanguage);
-        PostTask.postTask(TaskTraits.USER_BLOCKING, mExtractTask);
-    }
-
-    private File getAppDataDir() {
-        return new File(PathUtils.getDataDirectory());
-    }
-
-    private File getOutputDir() {
-        return new File(getAppDataDir(), "paks");
-    }
-
-    private void deleteFiles(String[] existingFileNames) {
-        // These used to be extracted, but no longer are, so just clean them up.
-        FileUtils.recursivelyDeleteFile(
-                new File(getAppDataDir(), ICU_DATA_FILENAME), FileUtils.DELETE_ALL);
-        FileUtils.recursivelyDeleteFile(
-                new File(getAppDataDir(), V8_SNAPSHOT_DATA_FILENAME), FileUtils.DELETE_ALL);
-
-        if (existingFileNames != null) {
-            for (String fileName : existingFileNames) {
-                FileUtils.recursivelyDeleteFile(
-                        new File(getOutputDir(), fileName), FileUtils.DELETE_ALL);
-            }
-        }
-    }
-
-    /**
-     * Pak extraction not necessarily required by the embedder.
-     */
-    private static boolean shouldSkipPakExtraction() {
-        // Certain apks like ContentShell.apk don't have any compressed locale
-        // assets however, so skip extraction entirely for them.
-        return ResourceBundle.getAvailableCompressedPakLocales().length == 0;
-    }
-}
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index ceffda9c..40a929d 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -44,7 +44,10 @@
 
 // Depending on the backend, the platform clipboard may or may not be
 // available.  Should it be absent, we provide a dummy one.  It always calls
-// back immediately with empty data, and denies ownership of any buffer.
+// back immediately with empty data. It starts without ownership of any buffers
+// but will take and keep ownership after a call to OfferClipboardData(). By
+// taking ownership, we allow ClipboardOzone to return existing data in
+// ReadClipboardDataAndWait().
 class StubPlatformClipboard : public PlatformClipboard {
  public:
   StubPlatformClipboard() = default;
@@ -55,6 +58,7 @@
       ClipboardBuffer buffer,
       const PlatformClipboard::DataMap& data_map,
       PlatformClipboard::OfferDataClosure callback) override {
+    is_owner_[buffer] = true;
     std::move(callback).Run();
   }
   void RequestClipboardData(
@@ -69,10 +73,15 @@
       PlatformClipboard::GetMimeTypesClosure callback) override {
     std::move(callback).Run({});
   }
-  bool IsSelectionOwner(ClipboardBuffer buffer) override { return false; }
+  bool IsSelectionOwner(ClipboardBuffer buffer) override {
+    return is_owner_[buffer];
+  }
   void SetSequenceNumberUpdateCb(
       PlatformClipboard::SequenceNumberUpdateCb cb) override {}
   bool IsSelectionBufferAvailable() const override { return false; }
+
+ private:
+  base::flat_map<ClipboardBuffer, bool> is_owner_;
 };
 
 }  // namespace
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index c5211b08..910662d2 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -372,22 +372,8 @@
 
   base::FilePath locale_file_path;
   if (base::PathService::Get(ui::DIR_LOCALES, &locale_file_path)) {
-#if defined(OS_ANDROID)
-    if (locale_file_path.value().find("chromium_tests") == std::string::npos) {
-      std::string extracted_file_suffix =
-          base::android::BuildInfo::GetInstance()->extracted_file_suffix();
-      locale_file_path = locale_file_path.AppendASCII(
-          app_locale + kPakFileExtension + extracted_file_suffix);
-    } else {
-      // TODO(agrieve): Update tests to not side-load pak files and remove
-      //     this special-case. https://crbug.com/691719
-      locale_file_path =
-          locale_file_path.AppendASCII(app_locale + kPakFileExtension);
-    }
-#else
     locale_file_path =
         locale_file_path.AppendASCII(app_locale + kPakFileExtension);
-#endif
   }
 
   // Note: The delegate GetPathForLocalePack() override is currently only used
diff --git a/ui/base/resource/resource_bundle_android.cc b/ui/base/resource/resource_bundle_android.cc
index ddb0e045..bc5d528 100644
--- a/ui/base/resource/resource_bundle_android.cc
+++ b/ui/base/resource/resource_bundle_android.cc
@@ -106,6 +106,8 @@
   }
   if (!GetPathForAndroidLocalePakWithinApk(locale, in_split, log_error).empty())
     return true;
+
+  // Fall back to checking on disk, which is necessary only for tests.
   const auto path = GetLocaleFilePath(locale);
   return !path.empty() && base::PathExists(path);
 }
@@ -122,9 +124,7 @@
   // a) WebView strings, which are always stored uncompressed under
   //    assets/stored-locales/ inside the APK or App Bundle.
   //
-  // b) For APKs, the Chrome UI strings are stored under assets/locales/
-  //    in compressed form. The relevant pak files is extracted on startup
-  //    and stored on the /data partition, with a version-specific suffix.
+  // b) For APKs, the Chrome UI strings are stored under assets/locales/.
   //
   // c) For App Bundles, Chrome UI strings are stored uncompressed under
   //    assets/locales#lang_<lang>/ (where <lang> is an Android language code)
@@ -139,8 +139,7 @@
   //
   //    If false, try to load it from the app bundle specific location
   //    (e.g. locales#lang_<language>/<locale>.pak). If the latter does not
-  //    exist, try to lookup the extracted APK-specific locale .pak file
-  //    from /data/app/.../<locale>.pak@<version> instead.
+  //    exist, try to lookup the APK-specific locale .pak file.
   //
   //    g_locale_paks_in_apk is set by SetLocalePaksStoredInApk() which
   //    is called from the WebView startup code.
@@ -172,7 +171,7 @@
           LoadLocalePakFromApk(app_locale, true, &g_locale_pack_region);
     }
     if (g_locale_pack_fd < 0) {
-      // Otherwise, try to locate the extracted locale .pak file.
+      // Otherwise, try to locate the side-loaded locale .pak file (for tests).
       if (locale_file_path.empty()) {
         auto path = GetLocaleFilePath(app_locale);
         if (base::PathExists(path))
diff --git a/ui/file_manager/integration_tests/file_manager/files_tooltip.js b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
index 6c54ea25..20ecb9b6 100644
--- a/ui/file_manager/integration_tests/file_manager/files_tooltip.js
+++ b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
@@ -91,16 +91,6 @@
         await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
     chrome.test.assertEq(expectedLabelText, label.text);
 
-    // Focus another button that has a tooltip: the view button.
-    chrome.test.assertTrue(
-        await remoteCall.callRemoteTestUtil('focus', appId, [viewButton]));
-    await getActiveElementById(appId, 'view-button');
-
-    // Check: the view button tooltip should be visible.
-    label =
-        await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
-    chrome.test.assertEq('Switch to thumbnail view', label.text);
-
     // Focus an element that has no tooltip: the file-list.
     chrome.test.assertTrue(
         await remoteCall.callRemoteTestUtil('focus', appId, [fileList]));
@@ -127,74 +117,40 @@
   };
 
   /**
-   * Tests that tooltip is displayed when hovering an element with tooltip.
+   * Tests that tooltips display when hovering an element that has a tooltip.
    */
   testcase.filesTooltipMouseOver = async () => {
     const appId = await setupAndWaitUntilReady(
         RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
 
-    // Hover a button with tooltip.
-    let tooltip = await remoteCall.waitForElement(appId, tooltipQueryHidden);
+    // Check: initially the tooltip should be hidden.
+    await remoteCall.waitForElement(appId, tooltipQueryHidden);
+
+    // Mouse hover over a button that has a tooltip: the search button.
     chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
         'fakeMouseOver', appId, [searchButton]));
 
-    // The tooltip should be visible.
-    let expectedLabelText = await getExpectedLabelText('SEARCH_TEXT_LABEL');
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
-    let label =
+    // Check: the search button tooltip should be visible.
+    const expectedLabelText = await getExpectedLabelText('SEARCH_TEXT_LABEL');
+    const firstElement =
         await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
-    chrome.test.assertEq(expectedLabelText, label.text);
+    chrome.test.assertEq(expectedLabelText, firstElement.text);
 
-    // Hover another button with tooltip.
+    // Move the mouse away from the search button.
     chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-        'fakeMouseOver', appId, [viewButton]));
+        'fakeMouseOut', appId, [searchButton]));
 
-    // The tooltip should be visible.
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
-    label =
-        await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
-    chrome.test.assertEq('Switch to thumbnail view', label.text);
+    // Check: the tooltip should hide.
+    await remoteCall.waitForElement(appId, tooltipQueryHidden);
 
-    // Go to readonly volume to have readonly indicator.
-    await remoteCall.simulateUiClick(
-        appId, ['[volume-type-for-testing=android_files]']);
-
-    // Hover another element with card tooltip.
-    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-        'fakeMouseOver', appId, [readonlyIndicator]));
-
-    // The tooltip should be visible.
-    expectedLabelText =
-        await getExpectedLabelText('READONLY_INDICATOR_TOOLTIP');
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
-    label =
-        await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
-    chrome.test.assertEq(expectedLabelText, label.text);
-    chrome.test.assertEq('card-tooltip', tooltip.attributes['class']);
-    chrome.test.assertEq('card-label', label.attributes['class']);
-
-    // Send a mouseout event.
-    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-        'fakeMouseOut', appId, [readonlyIndicator]));
-
-    // The tooltip should be hidden.
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryHidden);
-
-    // Hover a button with ordinary tooltip again.
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryHidden);
+    // Move the mouse over the search button again.
     chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
         'fakeMouseOver', appId, [searchButton]));
 
-    // The tooltip should be visible.
-    expectedLabelText = await getExpectedLabelText('SEARCH_TEXT_LABEL');
-    tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
-    label =
+    // Check: the search button tooltip should be visible.
+    const lastElement =
         await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
-    chrome.test.assertEq(expectedLabelText, label.text);
-
-    // Tooltip class should be cleared as ordinary tooltip shown.
-    chrome.test.assertEq('', label.attributes['class']);
-    chrome.test.assertEq('', tooltip.attributes['class']);
+    chrome.test.assertEq(expectedLabelText, lastElement.text);
   };
 
   /**
@@ -232,25 +188,36 @@
     const appId = await setupAndWaitUntilReady(
         RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
 
-    // Hover a button with tooltip.
-    await remoteCall.waitForElement(appId, tooltipQueryHidden);
-
-    // Go to a readonly volume to have the readonly indicator.
+    // Click the 'Android files' volume tab in the directory tree.
     await remoteCall.simulateUiClick(
         appId, ['[volume-type-for-testing=android_files]']);
 
-    // Hover an element with card tooltip.
+    // Wait for the read-only bubble to appear in the files app tool bar.
+    const readonlyBubbleShown = '#read-only-indicator:not([hidden])';
+    await remoteCall.waitForElement(appId, readonlyBubbleShown);
+
+    // Check: initially, no tooltip should be visible.
+    await remoteCall.waitForElement(appId, tooltipQueryHidden);
+
+    // Hover the mouse over the read-only bubble.
     chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
         'fakeMouseOver', appId, [readonlyIndicator]));
 
-    // The tooltip should be visible.
-    await remoteCall.waitForElement(appId, tooltipQueryVisible);
+    // Check: the read-only bubble card tooltip should be visible.
+    const expectedLabelText =
+        await getExpectedLabelText('READONLY_INDICATOR_TOOLTIP');
+    const tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
+    const label =
+        await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
+    chrome.test.assertEq(expectedLabelText, label.text);
+    chrome.test.assertEq('card-tooltip', tooltip.attributes['class']);
+    chrome.test.assertEq('card-label', label.attributes['class']);
 
-    // Click on body to hide tooltip.
+    // Click the body element.
     chrome.test.assertTrue(
         await remoteCall.callRemoteTestUtil('fakeMouseClick', appId, ['body']));
 
-    // The tooltip should be hidden.
+    // Check: the tooltip should hide.
     await remoteCall.waitForElement(appId, tooltipQueryHidden);
   };
 
@@ -261,20 +228,24 @@
     const appId = await setupAndWaitUntilReady(
         RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
 
-    // The tooltip should be hidden.
+    // Check: initially the tooltip should be hidden.
     await remoteCall.waitForElement(appId, tooltipQueryHidden);
 
-    // Focus a button with tooltip.
+    // Focus a button with tooltip: the search button.
     chrome.test.assertTrue(
         await remoteCall.callRemoteTestUtil('focus', appId, [searchButton]));
+    await getActiveElementById(appId, 'search-button');
 
-    // The tooltip should be visible.
-    await remoteCall.waitForElement(appId, tooltipQueryVisible);
+    // Check: the search button tooltip should be visible.
+    const expectedLabelText = await getExpectedLabelText('SEARCH_TEXT_LABEL');
+    const label =
+        await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
+    chrome.test.assertEq(expectedLabelText, label.text);
 
     // Resize the window.
     await remoteCall.callRemoteTestUtil('resizeWindow', appId, [1200, 1200]);
 
-    // The tooltip should be hidden.
+    // Check: the tooltip should hide.
     await remoteCall.waitForElement(appId, tooltipQueryHidden);
   };
 
diff --git a/ui/views/controls/menu/menu_config.h b/ui/views/controls/menu/menu_config.h
index c84d1377..ade60b9 100644
--- a/ui/views/controls/menu/menu_config.h
+++ b/ui/views/controls/menu/menu_config.h
@@ -184,8 +184,11 @@
   // Height of child MenuItemViews for touchable menus.
   int touchable_menu_height = 36;
 
-  // Width of touchable menus.
-  int touchable_menu_width = 256;
+  // Minimum width of touchable menus.
+  int touchable_menu_min_width = 256;
+
+  // Maximum width of touchable menus.
+  int touchable_menu_max_width = 352;
 
   // Shadow elevation of touchable menus.
   int touchable_menu_shadow_elevation = 12;
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 867971462..b963175 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2569,7 +2569,7 @@
     const bool create_on_right = prefer_leading != layout_is_rtl;
 
     const int width_with_right_inset =
-        menu_config.touchable_menu_width + border_and_shadow_insets.right();
+        menu_config.touchable_menu_min_width + border_and_shadow_insets.right();
     const int x_max = monitor_bounds.right() - width_with_right_inset;
     const int x_left = item_bounds.x() - width_with_right_inset;
     const int x_right = item_bounds.right() - border_and_shadow_insets.left();
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index caa8e35..383452ec 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -2164,7 +2164,7 @@
   MenuItemView* sub_item = menu_item()->GetSubmenu()->GetMenuItemAt(0);
   sub_item->AppendMenuItem(11, base::ASCIIToUTF16("Subitem.One"));
 
-  const int menu_width = MenuConfig::instance().touchable_menu_width;
+  const int menu_width = MenuConfig::instance().touchable_menu_min_width;
   const gfx::Size parent_size(menu_width, menu_width);
   const gfx::Size parent_size_wide(menu_width * 2, menu_width);
 
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index ab23f82..89f6c65 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -1249,6 +1249,9 @@
   dimensions.children_width = child_size.width();
   const MenuConfig& menu_config = MenuConfig::instance();
 
+  MenuDelegate::LabelStyle style;
+  GetLabelStyle(&style);
+
   if (GetMenuController() && GetMenuController()->use_touchable_layout()) {
     dimensions.height = menu_config.touchable_menu_height;
 
@@ -1259,7 +1262,15 @@
     if (IsContainer())
       return dimensions;
 
-    dimensions.standard_width = menu_config.touchable_menu_width;
+    // Calculate total item width to make sure the current |title_|
+    // has enough room within the context menu.
+    int label_start = GetLabelStartForThisItem();
+    int string_width = gfx::GetStringWidth(title_, style.font_list);
+    int item_width = string_width + label_start + item_right_margin_;
+
+    item_width = std::max(item_width, menu_config.touchable_menu_min_width);
+    item_width = std::min(item_width, menu_config.touchable_menu_max_width);
+    dimensions.standard_width = item_width;
 
     if (icon_view_) {
       dimensions.height = icon_view_->GetPreferredSize().height() +
@@ -1268,8 +1279,6 @@
     return dimensions;
   }
 
-  MenuDelegate::LabelStyle style;
-  GetLabelStyle(&style);
   base::string16 minor_text = GetMinorText();
 
   dimensions.height = child_size.height();
@@ -1358,7 +1367,8 @@
   // Touchable items with icons do not respect |label_start_|.
   if (GetMenuController() && GetMenuController()->use_touchable_layout() &&
       icon_view_) {
-    return 2 * config.touchable_item_horizontal_padding + icon_view_->width();
+    return 2 * config.touchable_item_horizontal_padding +
+           icon_view_->GetPreferredSize().width();
   }
 
   int label_start = label_start_ + left_icon_margin_ + right_icon_margin_;
diff --git a/ui/views/controls/menu/menu_item_view_unittest.cc b/ui/views/controls/menu/menu_item_view_unittest.cc
index b942a3d..cff5dcf 100644
--- a/ui/views/controls/menu/menu_item_view_unittest.cc
+++ b/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -16,6 +16,7 @@
 #include "ui/gfx/geometry/insets.h"
 #include "ui/native_theme/themed_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
+#include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/controls/menu/test_menu_item_view.h"
 #include "ui/views/test/menu_test_utils.h"
@@ -205,6 +206,67 @@
   EXPECT_FALSE(is_selected);
 }
 
+class TouchableMenuItemViewTest : public ViewsTestBase {
+ public:
+  TouchableMenuItemViewTest() = default;
+  ~TouchableMenuItemViewTest() override = default;
+
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+    widget_ = CreateTestWidget();
+    widget_->Show();
+
+    menu_delegate_ = std::make_unique<test::TestMenuDelegate>();
+    menu_item_view_ = new TestMenuItemView(menu_delegate_.get());
+    menu_runner_ = std::make_unique<MenuRunner>(
+        menu_item_view_, MenuRunner::USE_TOUCHABLE_LAYOUT);
+    menu_runner_->RunMenuAt(widget_.get(), nullptr, gfx::Rect(),
+                            MenuAnchorPosition::kTopLeft,
+                            ui::MENU_SOURCE_KEYBOARD);
+  }
+
+  void TearDown() override {
+    widget_->CloseNow();
+    ViewsTestBase::TearDown();
+  }
+
+  gfx::Size AppendItemAndGetSize(int i, const base::string16& title) {
+    return menu_item_view_->AppendMenuItem(i, title)->GetPreferredSize();
+  }
+
+ private:
+  std::unique_ptr<test::TestMenuDelegate> menu_delegate_;
+  std::unique_ptr<MenuRunner> menu_runner_;
+  std::unique_ptr<Widget> widget_;
+
+  // Owned by MenuRunner.
+  TestMenuItemView* menu_item_view_ = nullptr;
+};
+
+// Test that touchable menu items are sized to fit the menu item titles within
+// the allowed minimum and maximum width.
+TEST_F(TouchableMenuItemViewTest, MinAndMaxWidth) {
+  const int min_menu_width = MenuConfig::instance().touchable_menu_min_width;
+  const int max_menu_width = MenuConfig::instance().touchable_menu_max_width;
+
+  // Test a title shorter than the minimum width.
+  gfx::Size item1_size =
+      AppendItemAndGetSize(1, base::ASCIIToUTF16("Item1 Short title"));
+  EXPECT_EQ(item1_size.width(), min_menu_width);
+
+  // Test a title which is between the min and max allowed widths.
+  gfx::Size item2_size = AppendItemAndGetSize(
+      2, base::ASCIIToUTF16("Item2 bigger than min less than max"));
+  EXPECT_GT(item2_size.width(), min_menu_width);
+  EXPECT_LT(item2_size.width(), max_menu_width);
+
+  // Test a title which is longer than the max touchable menu width.
+  gfx::Size item3_size = AppendItemAndGetSize(
+      3, base::ASCIIToUTF16("Item3 Title that is longer than the maximum "
+                            "allowed context menu width"));
+  EXPECT_EQ(item3_size.width(), max_menu_width);
+}
+
 class MenuItemViewLayoutTest : public ViewsTestBase {
  public:
   MenuItemViewLayoutTest() : test_item_(root_menu_.AppendMenuItem(1)) {}
diff --git a/ui/views/controls/webview/webview_unittest.cc b/ui/views/controls/webview/webview_unittest.cc
index 5b9b0304..39005fa 100644
--- a/ui/views/controls/webview/webview_unittest.cc
+++ b/ui/views/controls/webview/webview_unittest.cc
@@ -191,6 +191,11 @@
         content::WebContents::CreateParams(browser_context_.get()));
   }
 
+  std::unique_ptr<content::WebContents> CreateTestWebContents() const {
+    return content::WebContentsTester::CreateTestWebContents(
+        browser_context_.get(), /*site_instnace=*/nullptr);
+  }
+
  private:
   std::unique_ptr<content::RenderViewHostTestEnabler> rvh_enabler_;
   std::unique_ptr<content::TestBrowserContext> browser_context_;
@@ -321,7 +326,11 @@
 // Test that the specified crashed overlay view is shown when a WebContents
 // is in a crashed state.
 TEST_F(WebViewUnitTest, CrashedOverlayView) {
-  const std::unique_ptr<content::WebContents> web_contents(CreateWebContents());
+  const std::unique_ptr<content::WebContents> web_contents =
+      CreateTestWebContents();
+  content::WebContentsTester* tester =
+      content::WebContentsTester::For(web_contents.get());
+
   std::unique_ptr<WebView> web_view(
       new WebView(web_contents->GetBrowserContext()));
   View* contents_view = top_level_widget()->GetContentsView();
@@ -337,7 +346,7 @@
   // WebContents, simulate that by calling SetIsCrashed and then
   // explicitly calling RenderFrameDeleted on the WebView to trigger it
   // to swap in the crashed overlay view.
-  web_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  tester->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
   EXPECT_TRUE(web_contents->IsCrashed());
   static_cast<content::WebContentsObserver*>(web_view.get())
       ->RenderFrameDeleted(web_contents->GetMainFrame());
@@ -346,7 +355,10 @@
 
 // Test that a crashed overlay view isn't deleted if it's owned by client.
 TEST_F(WebViewUnitTest, CrashedOverlayViewOwnedbyClient) {
-  const std::unique_ptr<content::WebContents> web_contents(CreateWebContents());
+  const std::unique_ptr<content::WebContents> web_contents =
+      CreateTestWebContents();
+  content::WebContentsTester* tester =
+      content::WebContentsTester::For(web_contents.get());
   std::unique_ptr<WebView> web_view(
       new WebView(web_contents->GetBrowserContext()));
   View* contents_view = top_level_widget()->GetContentsView();
@@ -359,7 +371,7 @@
   EXPECT_FALSE(crashed_overlay_view->IsDrawn());
 
   // Simulate a renderer crash (see above).
-  web_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+  tester->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
   EXPECT_TRUE(web_contents->IsCrashed());
   static_cast<content::WebContentsObserver*>(web_view.get())
       ->RenderFrameDeleted(web_contents->GetMainFrame());
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn b/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
index 493a1a0..1fb38add 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
@@ -165,6 +165,7 @@
     ":esim_flow_ui",
     ":psim_flow_ui",
     ":setup_selection_flow",
+    "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider",
     "//ui/webui/resources/js:i18n_behavior",
   ]
 }
@@ -418,6 +419,7 @@
     ":esim_flow_ui.m",
     ":psim_flow_ui.m",
     ":setup_selection_flow.m",
+    "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":cellular_setup_module" ]
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
index ffa81e4..a287341a 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
@@ -7,6 +7,7 @@
 <link rel="import" href="cellular_setup_delegate.html">
 <link rel="import" href="setup_selection_flow.html">
 <link rel="import" href="../../../html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/cr_components/chromeos/network/mojo_interface_provider.html">
 
 <dom-module id="cellular-setup">
   <template>
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
index 8e6a536..dbcdb67 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
@@ -24,10 +24,7 @@
      * Name of the currently displayed sub-page.
      * @private {!cellularSetup.CellularSetupPageName|null}
      */
-    currentPageName: {
-      type: String,
-      value: cellularSetup.CellularSetupPageName.SETUP_FLOW_SELECTION,
-    },
+    currentPageName: String,
 
     /**
      * Current user selected setup flow page name.
@@ -74,6 +71,57 @@
     'cancel-requested': 'onCancelRequested_',
   },
 
+
+  /** @override */
+  attached() {
+    if (!this.currentPageName) {
+      const networkConfig =
+          network_config.MojoInterfaceProviderImpl.getInstance()
+              .getMojoServiceRemote();
+      networkConfig.getDeviceStateList().then(response => {
+        this.setCurrentPage_(response.result);
+      });
+    }
+  },
+
+  /**
+   * @param {!Array<!chromeos.networkConfig.mojom.DeviceStateProperties>}
+   *     deviceStateList
+   * @private
+   */
+  setCurrentPage_(deviceStateList) {
+    let pSimSlots = 0;
+    let eSimSlots = 0;
+
+    const device = deviceStateList.find(
+        (device) =>
+            device.type === chromeos.networkConfig.mojom.NetworkType.kCellular);
+
+    if (!device || !device.simInfos) {
+      this.currentPageName =
+          cellularSetup.CellularSetupPageName.SETUP_FLOW_SELECTION;
+      return;
+    }
+
+    for (const simInfo of device.simInfos) {
+      if (simInfo.eid) {
+        eSimSlots++;
+        continue;
+      }
+      pSimSlots++;
+    }
+
+    if (pSimSlots > 0 && eSimSlots === 0) {
+      this.currentPageName = cellularSetup.CellularSetupPageName.PSIM_FLOW_UI;
+      return;
+    } else if (pSimSlots === 0 && eSimSlots > 0) {
+      this.currentPageName = cellularSetup.CellularSetupPageName.ESIM_FLOW_UI;
+      return;
+    }
+    this.currentPageName =
+        cellularSetup.CellularSetupPageName.SETUP_FLOW_SELECTION;
+  },
+
   /** @private */
   onPageChange_() {
     if (this.currentPage_) {
diff --git a/weblayer/app/content_main_delegate_impl.cc b/weblayer/app/content_main_delegate_impl.cc
index e92cd66..c2a45cd 100644
--- a/weblayer/app/content_main_delegate_impl.cc
+++ b/weblayer/app/content_main_delegate_impl.cc
@@ -23,6 +23,7 @@
 #include "content/public/common/url_constants.h"
 #include "media/base/media_switches.h"
 #include "services/network/public/cpp/features.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_paths.h"
@@ -166,6 +167,8 @@
     ::features::kPeriodicBackgroundSync,
     // TODO(crbug.com/1131017): Support SurfaceViews on WebLayer.
     media::kOverlayFullscreenVideo,
+    // TODO(crbug.com/1174856): Support Portals.
+    blink::features::kPortals,
 #if defined(OS_ANDROID)
     // TODO(crbug.com/1131016): Support Picture in Picture API on WebLayer.
     media::kPictureInPictureAPI,
diff --git a/weblayer/browser/page_load_metrics_initialize.cc b/weblayer/browser/page_load_metrics_initialize.cc
index 037f984..ab7b884 100644
--- a/weblayer/browser/page_load_metrics_initialize.cc
+++ b/weblayer/browser/page_load_metrics_initialize.cc
@@ -12,6 +12,14 @@
 #include "weblayer/browser/no_state_prefetch/prerender_utils.h"
 #include "weblayer/browser/page_load_metrics_observer_impl.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace page_load_metrics {
+class PageLoadMetricsMemoryTracker;
+}  // namespace page_load_metrics
+
 namespace weblayer {
 
 namespace {
@@ -34,6 +42,11 @@
     return NoStatePrefetchContentsFromWebContents(web_contents);
   }
   bool IsExtensionUrl(const GURL& url) override { return false; }
+  page_load_metrics::PageLoadMetricsMemoryTracker*
+  GetMemoryTrackerForBrowserContext(
+      content::BrowserContext* browser_context) override {
+    return nullptr;
+  }
 
  protected:
   // page_load_metrics::PageLoadMetricsEmbedderBase:
diff --git a/weblayer/browser/translate_client_impl.cc b/weblayer/browser/translate_client_impl.cc
index 9bfc06e..d9b23f0 100644
--- a/weblayer/browser/translate_client_impl.cc
+++ b/weblayer/browser/translate_client_impl.cc
@@ -53,7 +53,8 @@
 TranslateClientImpl::TranslateClientImpl(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       translate_driver_(&web_contents->GetController(),
-                        /*url_language_histogram=*/nullptr),
+                        /*url_language_histogram=*/nullptr,
+                        /*translate_model_service=*/nullptr),
       translate_manager_(new translate::TranslateManager(
           this,
           TranslateRankerFactory::GetForBrowserContext(